From 78b03b69965ac03ed827e4685f7ac1ba88c05ed6 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 21 Sep 2026 09:34:49 +0200 Subject: [PATCH 1/4] Colocated entity type configurations with entities Added tenant entities and models Modified db context --- .../Configuration/Entities/Activity.cs | 57 ----------------- .../Configuration/Entities/AuditLog.cs | 25 -------- .../Configuration/Entities/Award.cs | 58 ----------------- .../Configuration/Entities/Beneficiary.cs | 40 ------------ .../Configuration/Entities/Budget.cs | 38 ------------ .../Configuration/Entities/ChangeRequest.cs | 42 ------------- .../Configuration/Entities/ComplianceItem.cs | 40 ------------ .../Configuration/Entities/Contract.cs | 35 ----------- .../Configuration/Entities/Disbursement.cs | 44 ------------- .../Configuration/Entities/Evidence.cs | 61 ------------------ .../Configuration/Entities/Indicator.cs | 33 ---------- .../Configuration/Entities/Instrument.cs | 33 ---------- .../Configuration/Entities/Invite.cs | 51 --------------- .../Configuration/Entities/Issue.cs | 43 ------------- .../Configuration/Entities/Milestone.cs | 42 ------------- .../Configuration/Entities/Notification.cs | 38 ------------ .../Configuration/Entities/Organisation.cs | 28 --------- .../Configuration/Entities/Outcome.cs | 30 --------- .../Configuration/Entities/Portfolio.cs | 37 ----------- .../Configuration/Entities/Programme.cs | 40 ------------ .../Configuration/Entities/Project.cs | 46 -------------- .../Configuration/Entities/Risk.cs | 34 ---------- .../Configuration/Entities/Rule.cs | 40 ------------ .../Configuration/Entities/SiteVisit.cs | 33 ---------- .../Configuration/Entities/User.cs | 17 ----- .../Entities/Activity.cs | 58 ++++++++++++++++- .../Entities/AuditLog.cs | 26 +++++++- PostFundManagement.Domain/Entities/Award.cs | 59 +++++++++++++++++- .../Entities/Beneficiary.cs | 41 +++++++++++- PostFundManagement.Domain/Entities/Budget.cs | 39 +++++++++++- .../Entities/ChangeRequest.cs | 43 ++++++++++++- .../Entities/ComplianceItem.cs | 41 +++++++++++- .../Entities/Contract.cs | 36 ++++++++++- .../Entities/Disbursement.cs | 45 +++++++++++++- .../Entities/Evidence.cs | 62 ++++++++++++++++++- .../Entities/Indicator.cs | 34 +++++++++- .../Entities/Instrument.cs | 36 ++++++++++- PostFundManagement.Domain/Entities/Invite.cs | 51 ++++++++++++++- PostFundManagement.Domain/Entities/Issue.cs | 44 ++++++++++++- .../Entities/Milestone.cs | 43 ++++++++++++- .../Entities/Notification.cs | 39 +++++++++++- .../Entities/Organisation.cs | 29 ++++++++- PostFundManagement.Domain/Entities/Outcome.cs | 31 +++++++++- .../Entities/Portfolio.cs | 38 +++++++++++- .../Entities/Programme.cs | 41 +++++++++++- PostFundManagement.Domain/Entities/Project.cs | 47 +++++++++++++- PostFundManagement.Domain/Entities/Risk.cs | 35 ++++++++++- PostFundManagement.Domain/Entities/Rule.cs | 41 +++++++++++- .../Entities/SiteVisit.cs | 34 +++++++++- PostFundManagement.Domain/Entities/Tenant.cs | 40 ++++++++++++ .../Entities/TenantOrganisation.cs | 45 ++++++++++++++ .../Entities/TenantUser.cs | 39 ++++++++++++ PostFundManagement.Domain/Entities/User.cs | 20 +++++- PostFundManagement.Domain/Models/Tenant.cs | 18 ++++++ .../Models/TenantOrganisation.cs | 16 +++++ .../Models/TenantUser.cs | 16 +++++ .../PostFundManagement.Domain.csproj | 8 +++ .../Database/ApplicationDbContext.cs | 6 ++ 58 files changed, 1174 insertions(+), 1012 deletions(-) delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Activity.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/AuditLog.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Award.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Beneficiary.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Budget.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/ChangeRequest.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/ComplianceItem.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Contract.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Disbursement.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Evidence.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Indicator.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Instrument.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Invite.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Issue.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Milestone.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Notification.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Organisation.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Outcome.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Portfolio.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Programme.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Project.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Risk.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/Rule.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/SiteVisit.cs delete mode 100644 PostFundManagement.Domain/Configuration/Entities/User.cs create mode 100644 PostFundManagement.Domain/Entities/Tenant.cs create mode 100644 PostFundManagement.Domain/Entities/TenantOrganisation.cs create mode 100644 PostFundManagement.Domain/Entities/TenantUser.cs create mode 100644 PostFundManagement.Domain/Models/Tenant.cs create mode 100644 PostFundManagement.Domain/Models/TenantOrganisation.cs create mode 100644 PostFundManagement.Domain/Models/TenantUser.cs diff --git a/PostFundManagement.Domain/Configuration/Entities/Activity.cs b/PostFundManagement.Domain/Configuration/Entities/Activity.cs deleted file mode 100644 index 040c9eb..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Activity.cs +++ /dev/null @@ -1,57 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Activity : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Activity).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.MilestoneId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.ApprovedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.PerformedAt).IsRequired(false); - builder.Property(f => f.Category).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Status).IsRequired().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); - builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Milestone) - .WithMany() - .HasForeignKey(fk => fk.MilestoneId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Updator) - .WithMany() - .HasForeignKey(fk => fk.UpdatedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Approver) - .WithMany() - .HasForeignKey(fk => fk.ApprovedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/AuditLog.cs b/PostFundManagement.Domain/Configuration/Entities/AuditLog.cs deleted file mode 100644 index f99df02..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/AuditLog.cs +++ /dev/null @@ -1,25 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class AuditLog : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.AuditLog).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.EntityName).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.EntityId).IsRequired(); - builder.Property(f => f.Action).IsRequired().HasMaxLength(ShortLabelLength); - builder.Property(f => f.Changes).IsRequired(false).HasColumnType("jsonb"); - builder.Property(f => f.PerformedBy).IsRequired(); - builder.Property(f => f.Timestamp).IsRequired().HasDefaultValueSql("now()"); - - builder.HasOne(f => f.Performer) - .WithMany() - .HasForeignKey(fk => fk.PerformedBy) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Award.cs b/PostFundManagement.Domain/Configuration/Entities/Award.cs deleted file mode 100644 index c6d2dbb..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Award.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Award : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Award).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.OrganisationId).IsRequired(); - builder.Property(f => f.ProgrammeId).IsRequired(); - builder.Property(f => f.ProjectId).IsRequired(false); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.ApprovedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); - - builder.HasOne(f => f.Organisation) - .WithMany() - .HasForeignKey(fk => fk.OrganisationId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Programme) - .WithMany(f => f.Awards) - .HasForeignKey(fk => fk.ProgrammeId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Project) - .WithMany(f => f.Awards) - .HasForeignKey(fk => fk.ProjectId) - .IsRequired(false) - .OnDelete(DeleteBehavior.SetNull); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Updator) - .WithMany() - .HasForeignKey(fk => fk.UpdatedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Approver) - .WithMany() - .HasForeignKey(fk => fk.ApprovedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Beneficiary.cs b/PostFundManagement.Domain/Configuration/Entities/Beneficiary.cs deleted file mode 100644 index d9762a5..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Beneficiary.cs +++ /dev/null @@ -1,40 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Beneficiary : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Beneficiary).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UserId).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Category).IsRequired().HasMaxLength(ShortLabelLength); - builder.Property(f => f.TargetCount).IsRequired(); - builder.Property(f => f.ReachedCount).IsRequired(); - builder.Property(f => f.Location).IsRequired(); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.User) - .WithMany() - .HasForeignKey(fk => fk.UserId) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Budget.cs b/PostFundManagement.Domain/Configuration/Entities/Budget.cs deleted file mode 100644 index 6fde58a..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Budget.cs +++ /dev/null @@ -1,38 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Budget : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Budget).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.ApprovedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Approver) - .WithMany() - .HasForeignKey(fk => fk.ApprovedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/ChangeRequest.cs b/PostFundManagement.Domain/Configuration/Entities/ChangeRequest.cs deleted file mode 100644 index 433a7c8..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/ChangeRequest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class ChangeRequest : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.ChangeRequest).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.ApprovedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Justification).IsRequired().HasMaxLength(TextLength); - builder.Property(f => f.RevisedBudget).IsRequired(false).HasPrecision(18, 2); - builder.Property(f => f.RevisedEndedAt).IsRequired(false); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Approver) - .WithMany() - .HasForeignKey(fk => fk.ApprovedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/ComplianceItem.cs b/PostFundManagement.Domain/Configuration/Entities/ComplianceItem.cs deleted file mode 100644 index dff70db..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/ComplianceItem.cs +++ /dev/null @@ -1,40 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class ComplianceItem : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.ComplianceItem).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.VerifiedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Requirements).IsRequired().HasMaxLength(MediumTextLength); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Verifier) - .WithMany() - .HasForeignKey(fk => fk.VerifiedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Contract.cs b/PostFundManagement.Domain/Configuration/Entities/Contract.cs deleted file mode 100644 index 60c4eca..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Contract.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Contract : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Contract).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.HasIndex(f => f.AwardId).IsUnique(); - - builder.Property(f => f.AwardId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.ExternalSignatureId).IsRequired(false); - builder.Property(f => f.SignedDocumentUrl).IsRequired(false); - builder.Property(f => f.EffectiveAt).IsRequired(false); - builder.Property(f => f.ExpiresAt).IsRequired(false); - builder.Property(f => f.TotalValue).IsRequired().HasPrecision(18, 2); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ContractStatus.Draft); - - builder.HasOne(f => f.Award) - .WithOne(f => f.Contract) - .HasForeignKey(fk => fk.AwardId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Disbursement.cs b/PostFundManagement.Domain/Configuration/Entities/Disbursement.cs deleted file mode 100644 index b95016c..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Disbursement.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Disbursement : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Disbursement).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.MilestoneId).IsRequired(false); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Milestone) - .WithMany() - .HasForeignKey(fk => fk.MilestoneId) - .IsRequired(false) - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Updator) - .WithMany() - .HasForeignKey(fk => fk.UpdatedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Evidence.cs b/PostFundManagement.Domain/Configuration/Entities/Evidence.cs deleted file mode 100644 index 998b1b5..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Evidence.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Evidence : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Evidence).Pluralize()); - - builder.HasKey(f => f.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.MilestoneId).IsRequired(); - builder.Property(f => f.IndicatorId).IsRequired(); - builder.Property(f => f.ActivityId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Version).IsRequired().HasDefaultValue(1); - builder.Property(f => f.DocumentUrl).IsRequired(); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - builder.Property(f => f.Type).IsRequired().HasConversion().HasDefaultValueSql("9") - .HasSentinel(EvidenceType.ProcurementDocument); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Milestone) - .WithMany() - .HasForeignKey(fk => fk.MilestoneId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Indicator) - .WithMany() - .HasForeignKey(fk => fk.IndicatorId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Activity) - .WithMany(f => f.Evidences) - .HasForeignKey(fk => fk.ActivityId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Updator) - .WithMany() - .HasForeignKey(fk => fk.UpdatedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Indicator.cs b/PostFundManagement.Domain/Configuration/Entities/Indicator.cs deleted file mode 100644 index eefce24..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Indicator.cs +++ /dev/null @@ -1,33 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Indicator : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Indicator).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.UnitOfMeasure).IsRequired().HasConversion().HasDefaultValueSql("2"); - builder.Property(f => f.BaselineAmount).IsRequired().HasPrecision(18, 2); - builder.Property(f => f.TargetAmount).IsRequired().HasPrecision(18, 2); - builder.Property(f => f.ActualAmount).IsRequired().HasPrecision(18, 2); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Instrument.cs b/PostFundManagement.Domain/Configuration/Entities/Instrument.cs deleted file mode 100644 index 789594e..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Instrument.cs +++ /dev/null @@ -1,33 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Instrument : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Instrument).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProgrammeId).IsRequired(); - builder.Property(f => f.Code).IsRequired().HasMaxLength(ShortLabelLength); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("2") - .HasSentinel(ActivityStatus.Inactive); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - - builder.HasOne(f => f.Programme) - .WithMany(f => f.Instruments) - .HasForeignKey(fk => fk.ProgrammeId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} diff --git a/PostFundManagement.Domain/Configuration/Entities/Invite.cs b/PostFundManagement.Domain/Configuration/Entities/Invite.cs deleted file mode 100644 index 631fada..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Invite.cs +++ /dev/null @@ -1,51 +0,0 @@ - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Invite : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Invite).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.OrganisationId).IsRequired(); - builder.Property(f => f.InvitedOrganisationId).IsRequired(); - builder.Property(f => f.AwardId).IsRequired(); - builder.Property(f => f.ContractId).IsRequired(); - builder.Property(f => f.Recipient).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - - builder.HasOne(f => f.Organisation) - .WithMany() - .HasForeignKey(fk => fk.OrganisationId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.InvitedOrganisation) - .WithMany() - .HasForeignKey(fk => fk.InvitedOrganisationId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Award) - .WithMany() - .HasForeignKey(fk => fk.AwardId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Contract) - .WithMany() - .HasForeignKey(fk => fk.ContractId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.RecipientUser) - .WithMany() - .HasForeignKey(fk => fk.Recipient) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Issue.cs b/PostFundManagement.Domain/Configuration/Entities/Issue.cs deleted file mode 100644 index 49c7d33..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Issue.cs +++ /dev/null @@ -1,43 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Issue : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Issue).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.ResolvedAt).IsRequired(false); - builder.Property(f => f.AssignedTo).IsRequired(false); - builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); - builder.Property(f => f.Priority).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(Priority.Low); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ActivityStatus.Active); - builder.Property(f => f.Resolution).IsRequired(); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Assignee) - .WithMany() - .HasForeignKey(fk => fk.AssignedTo) - .IsRequired() - .OnDelete(DeleteBehavior.SetNull); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Milestone.cs b/PostFundManagement.Domain/Configuration/Entities/Milestone.cs deleted file mode 100644 index 513731b..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Milestone.cs +++ /dev/null @@ -1,42 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Milestone : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Milestone).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.DueAt).IsRequired(); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - builder.Property(f => f.Priority).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(Priority.Low); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Updator) - .WithMany() - .HasForeignKey(fk => fk.UpdatedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Notification.cs b/PostFundManagement.Domain/Configuration/Entities/Notification.cs deleted file mode 100644 index 563b988..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Notification.cs +++ /dev/null @@ -1,38 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Notification : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Notification).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.SentAt).IsRequired(false); - builder.Property(f => f.Recipient).IsRequired(); - builder.Property(f => f.OrganisationId).IsRequired(); - builder.Property(f => f.Platform).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(NotificationPlatform.Email); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(NotificationStatus.Pending); - builder.Property(f => f.Subject).IsRequired(false).HasMaxLength(LabelLength); - builder.Property(f => f.Message).IsRequired().HasMaxLength(LargeTextLength); - builder.Property(f => f.Destination).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Error).IsRequired(false); - - builder.HasOne(f => f.Organisation) - .WithMany() - .HasForeignKey(fk => fk.OrganisationId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.RecipientUser) - .WithMany() - .HasForeignKey(fk => fk.Recipient) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Organisation.cs b/PostFundManagement.Domain/Configuration/Entities/Organisation.cs deleted file mode 100644 index ddacef1..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Organisation.cs +++ /dev/null @@ -1,28 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Organisation : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Organisation).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.RegistrationNo).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Email).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Type).IsRequired().HasConversion().HasDefaultValueSql("5") - .HasSentinel(OrganisationType.PrivateLimitedCompany); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ActivityStatus.Active); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Outcome.cs b/PostFundManagement.Domain/Configuration/Entities/Outcome.cs deleted file mode 100644 index 28a7dfa..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Outcome.cs +++ /dev/null @@ -1,30 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Outcome : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Outcome).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.BeneficiaryId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); - - builder.HasOne(f => f.Beneficiary) - .WithMany() - .HasForeignKey(fk => fk.BeneficiaryId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Portfolio.cs b/PostFundManagement.Domain/Configuration/Entities/Portfolio.cs deleted file mode 100644 index 8ffc3ee..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Portfolio.cs +++ /dev/null @@ -1,37 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Portfolio : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Portfolio).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.OrganisationId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.OwnedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength); - - builder.HasOne(f => f.Organisation) - .WithMany() - .HasForeignKey(fk => fk.OrganisationId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Owner) - .WithMany() - .HasForeignKey(fk => fk.OwnedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Programme.cs b/PostFundManagement.Domain/Configuration/Entities/Programme.cs deleted file mode 100644 index d5180c4..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Programme.cs +++ /dev/null @@ -1,40 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Programme : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Programme).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.PortfolioId).IsRequired(); - builder.Property(f => f.OwnedBy).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - - builder.HasOne(f => f.Portfolio) - .WithMany(f => f.Programmes) - .HasForeignKey(fk => fk.PortfolioId) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Owner) - .WithMany() - .IsRequired() - .HasForeignKey(fk => fk.OwnedBy) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Project.cs b/PostFundManagement.Domain/Configuration/Entities/Project.cs deleted file mode 100644 index 4f55526..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Project.cs +++ /dev/null @@ -1,46 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Project : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Project).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProgrammeId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.StartedAt).IsRequired(false); - builder.Property(f => f.EndedAt).IsRequired(false); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); - builder.Property(f => f.Sector).IsRequired().HasConversion().HasDefaultValueSql("11") - .HasSentinel(IndustrySector.InformationCommunicationTechnology); - builder.Property(f => f.ProjectType).IsRequired().HasConversion().HasDefaultValueSql("10") - .HasSentinel(ProjectType.Innovation); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - - builder.HasOne(f => f.Programme) - .WithMany(f => f.Projects) - .HasForeignKey(fk => fk.ProgrammeId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Updator) - .WithMany() - .HasForeignKey(fk => fk.UpdatedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Risk.cs b/PostFundManagement.Domain/Configuration/Entities/Risk.cs deleted file mode 100644 index 3c731b0..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Risk.cs +++ /dev/null @@ -1,34 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Risk : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Risk).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.Likelihood).IsRequired().HasConversion().HasDefaultValueSql("3") - .HasSentinel(RiskLikelihood.Possible); - builder.Property(f => f.Impact).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(RiskImpact.Negligible); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/Rule.cs b/PostFundManagement.Domain/Configuration/Entities/Rule.cs deleted file mode 100644 index 0160edb..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/Rule.cs +++ /dev/null @@ -1,40 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class Rule : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.Rule).Pluralize()); - - builder.HasKey(fk => fk.Id); - builder.Property(f => f.ProgrammeId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.UpdatedBy).IsRequired(false); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Version).IsRequired().HasDefaultValue(1); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("2") - .HasSentinel(ActivityStatus.Inactive); - - builder.HasOne(f => f.Programme) - .WithMany(f => f.Rules) - .HasForeignKey(fk => fk.ProgrammeId) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Updator) - .WithMany() - .HasForeignKey(fk => fk.UpdatedBy) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/SiteVisit.cs b/PostFundManagement.Domain/Configuration/Entities/SiteVisit.cs deleted file mode 100644 index 116348e..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/SiteVisit.cs +++ /dev/null @@ -1,33 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class SiteVisit : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.SiteVisit).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.ProjectId).IsRequired(); - builder.Property(f => f.CreatedBy).IsRequired(); - builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); - builder.Property(f => f.VisitedAt).IsRequired(); - builder.Property(f => f.InspectorName).IsRequired(); - builder.Property(f => f.Findings).IsRequired(false).HasMaxLength(LargeTextLength); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") - .HasSentinel(ApprovalStatus.Pending); - - builder.HasOne(f => f.Project) - .WithMany() - .HasForeignKey(fk => fk.ProjectId) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Creator) - .WithMany() - .HasForeignKey(fk => fk.CreatedBy) - .IsRequired() - .OnDelete(DeleteBehavior.NoAction); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Configuration/Entities/User.cs b/PostFundManagement.Domain/Configuration/Entities/User.cs deleted file mode 100644 index 183e4f7..0000000 --- a/PostFundManagement.Domain/Configuration/Entities/User.cs +++ /dev/null @@ -1,17 +0,0 @@ -using static PostFundManagement.Domain.Extensions.Constants; - -namespace PostFundManagement.Domain.Configuration.Entities; - -public sealed class User : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Domain.Entities.User).Pluralize()); - - builder.HasKey(pk => pk.Id); - builder.Property(f => f.Email).IsRequired().HasMaxLength(LabelLength); - builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("2") - .HasSentinel(ActivityStatus.Inactive); - builder.Property(f => f.LastLoginAt).IsRequired(false); - } -} \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Activity.cs b/PostFundManagement.Domain/Entities/Activity.cs index 24ed074..5f2f657 100644 --- a/PostFundManagement.Domain/Entities/Activity.cs +++ b/PostFundManagement.Domain/Entities/Activity.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Activity : Models.Activity { public virtual User? Creator { get; set; } @@ -14,4 +16,58 @@ public class Activity : Models.Activity public virtual Milestone? Milestone { get; set; } public virtual ICollection Evidences { get; set; } = []; +} + +public sealed class ActivityConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Activity).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.MilestoneId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.ApprovedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.PerformedAt).IsRequired(false); + builder.Property(f => f.Category).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Status).IsRequired().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); + builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Milestone) + .WithMany() + .HasForeignKey(fk => fk.MilestoneId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Approver) + .WithMany() + .HasForeignKey(fk => fk.ApprovedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/AuditLog.cs b/PostFundManagement.Domain/Entities/AuditLog.cs index 34b32b5..53cf177 100644 --- a/PostFundManagement.Domain/Entities/AuditLog.cs +++ b/PostFundManagement.Domain/Entities/AuditLog.cs @@ -1,7 +1,31 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class AuditLog : Models.AuditLog { public virtual User? Performer { get; set; } +} + +public sealed class AuditLogConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Domain.Entities.AuditLog).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.EntityName).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.EntityId).IsRequired(); + builder.Property(f => f.Action).IsRequired().HasMaxLength(ShortLabelLength); + builder.Property(f => f.Changes).IsRequired(false).HasColumnType("jsonb"); + builder.Property(f => f.PerformedBy).IsRequired(); + builder.Property(f => f.Timestamp).IsRequired().HasDefaultValueSql("now()"); + + builder.HasOne(f => f.Performer) + .WithMany() + .HasForeignKey(fk => fk.PerformedBy) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Award.cs b/PostFundManagement.Domain/Entities/Award.cs index 4877d52..488d234 100644 --- a/PostFundManagement.Domain/Entities/Award.cs +++ b/PostFundManagement.Domain/Entities/Award.cs @@ -1,6 +1,6 @@ namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Award : Models.Award { public virtual User? Creator { get; set; } @@ -16,4 +16,61 @@ public class Award : Models.Award public virtual Project? Project { get; set; } public virtual Contract? Contract { get; set; } +} + +public sealed class AwardConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Award).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.OrganisationId).IsRequired(); + builder.Property(f => f.ProgrammeId).IsRequired(); + builder.Property(f => f.ProjectId).IsRequired(false); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.ApprovedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); + + builder.HasOne(f => f.Organisation) + .WithMany() + .HasForeignKey(fk => fk.OrganisationId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Programme) + .WithMany(f => f.Awards) + .HasForeignKey(fk => fk.ProgrammeId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Project) + .WithMany(f => f.Awards) + .HasForeignKey(fk => fk.ProjectId) + .IsRequired(false) + .OnDelete(DeleteBehavior.SetNull); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Approver) + .WithMany() + .HasForeignKey(fk => fk.ApprovedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Beneficiary.cs b/PostFundManagement.Domain/Entities/Beneficiary.cs index d7a1c81..24f246a 100644 --- a/PostFundManagement.Domain/Entities/Beneficiary.cs +++ b/PostFundManagement.Domain/Entities/Beneficiary.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Beneficiary : Models.Beneficiary { public virtual Project? Project { get; set; } @@ -8,4 +10,41 @@ public class Beneficiary : Models.Beneficiary public virtual User? Creator { get; set; } public virtual User? User { get; set; } +} + +public sealed class BeneficiaryConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Beneficiary).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UserId).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Category).IsRequired().HasMaxLength(ShortLabelLength); + builder.Property(f => f.TargetCount).IsRequired(); + builder.Property(f => f.ReachedCount).IsRequired(); + builder.Property(f => f.Location).IsRequired(); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.User) + .WithMany() + .HasForeignKey(fk => fk.UserId) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Budget.cs b/PostFundManagement.Domain/Entities/Budget.cs index 9dcdf0d..497d2b3 100644 --- a/PostFundManagement.Domain/Entities/Budget.cs +++ b/PostFundManagement.Domain/Entities/Budget.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Budget : Models.Budget { public virtual Project? Project { get; set; } @@ -8,4 +10,39 @@ public class Budget : Models.Budget public virtual User? Creator { get; set; } public virtual User? Approver { get; set; } +} + +public sealed class BudgetConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Budget).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.ApprovedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Approver) + .WithMany() + .HasForeignKey(fk => fk.ApprovedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/ChangeRequest.cs b/PostFundManagement.Domain/Entities/ChangeRequest.cs index 542b6cd..e4b4f11 100644 --- a/PostFundManagement.Domain/Entities/ChangeRequest.cs +++ b/PostFundManagement.Domain/Entities/ChangeRequest.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class ChangeRequest : Models.ChangeRequest { public virtual Project? Project { get; set; } @@ -8,4 +10,43 @@ public class ChangeRequest : Models.ChangeRequest public virtual User? Creator { get; set; } public virtual User? Approver { get; set; } +} + +public sealed class ChangeRequestConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(ChangeRequest).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.ApprovedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Justification).IsRequired().HasMaxLength(TextLength); + builder.Property(f => f.RevisedBudget).IsRequired(false).HasPrecision(18, 2); + builder.Property(f => f.RevisedEndedAt).IsRequired(false); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Approver) + .WithMany() + .HasForeignKey(fk => fk.ApprovedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/ComplianceItem.cs b/PostFundManagement.Domain/Entities/ComplianceItem.cs index a74e6fa..3ecf34a 100644 --- a/PostFundManagement.Domain/Entities/ComplianceItem.cs +++ b/PostFundManagement.Domain/Entities/ComplianceItem.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class ComplianceItem : Models.ComplianceItem { public virtual Project? Project { get; set; } @@ -8,4 +10,41 @@ public class ComplianceItem : Models.ComplianceItem public virtual User? Creator { get; set; } public virtual User? Verifier { get; set; } +} + +public sealed class ComplianceItemConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(ComplianceItem).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.VerifiedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Requirements).IsRequired().HasMaxLength(MediumTextLength); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Verifier) + .WithMany() + .HasForeignKey(fk => fk.VerifiedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Contract.cs b/PostFundManagement.Domain/Entities/Contract.cs index 4715b68..320c9d9 100644 --- a/PostFundManagement.Domain/Entities/Contract.cs +++ b/PostFundManagement.Domain/Entities/Contract.cs @@ -1,9 +1,43 @@ namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Contract : Models.Contract { public virtual Award? Award { get; set; } public virtual User? Creator { get; set; } +} + +public sealed class ContractConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Contract).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.HasIndex(f => f.AwardId).IsUnique(); + + builder.Property(f => f.AwardId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.ExternalSignatureId).IsRequired(false); + builder.Property(f => f.SignedDocumentUrl).IsRequired(false); + builder.Property(f => f.EffectiveAt).IsRequired(false); + builder.Property(f => f.ExpiresAt).IsRequired(false); + builder.Property(f => f.TotalValue).IsRequired().HasPrecision(18, 2); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ContractStatus.Draft); + + builder.HasOne(f => f.Award) + .WithOne(f => f.Contract) + .HasForeignKey(fk => fk.AwardId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Disbursement.cs b/PostFundManagement.Domain/Entities/Disbursement.cs index 185cdc9..e59dd54 100644 --- a/PostFundManagement.Domain/Entities/Disbursement.cs +++ b/PostFundManagement.Domain/Entities/Disbursement.cs @@ -1,6 +1,6 @@ namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Disbursement : Models.Disbursement { public virtual Project? Project { get; set; } @@ -10,4 +10,47 @@ public class Disbursement : Models.Disbursement public virtual User? Creator { get; set; } public virtual User? Updator { get; set; } +} + +public sealed class DisbursementConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Disbursement).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.MilestoneId).IsRequired(false); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Milestone) + .WithMany() + .HasForeignKey(fk => fk.MilestoneId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Evidence.cs b/PostFundManagement.Domain/Entities/Evidence.cs index 4f5cc62..2fdb509 100644 --- a/PostFundManagement.Domain/Entities/Evidence.cs +++ b/PostFundManagement.Domain/Entities/Evidence.cs @@ -1,6 +1,6 @@ namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Evidence : Models.Evidence { public virtual Project? Project { get; set; } @@ -14,4 +14,64 @@ public class Evidence : Models.Evidence public virtual User? Creator { get; set; } public virtual User? Updator { get; set; } +} + +public sealed class EvidenceConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Evidence).Pluralize()); + + builder.HasKey(f => f.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.MilestoneId).IsRequired(); + builder.Property(f => f.IndicatorId).IsRequired(); + builder.Property(f => f.ActivityId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Version).IsRequired().HasDefaultValue(1); + builder.Property(f => f.DocumentUrl).IsRequired(); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + builder.Property(f => f.Type).IsRequired().HasConversion().HasDefaultValueSql("9") + .HasSentinel(EvidenceType.ProcurementDocument); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Milestone) + .WithMany() + .HasForeignKey(fk => fk.MilestoneId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Indicator) + .WithMany() + .HasForeignKey(fk => fk.IndicatorId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Activity) + .WithMany(f => f.Evidences) + .HasForeignKey(fk => fk.ActivityId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Indicator.cs b/PostFundManagement.Domain/Entities/Indicator.cs index 24283af..7fbdbde 100644 --- a/PostFundManagement.Domain/Entities/Indicator.cs +++ b/PostFundManagement.Domain/Entities/Indicator.cs @@ -1,9 +1,41 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Indicator : Models.Indicator { public virtual Project? Project { get; set; } public virtual User? Creator { get; set; } +} + +public sealed class IndicatorConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Indicator).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.UnitOfMeasure).IsRequired().HasConversion().HasDefaultValueSql("2"); + builder.Property(f => f.BaselineAmount).IsRequired().HasPrecision(18, 2); + builder.Property(f => f.TargetAmount).IsRequired().HasPrecision(18, 2); + builder.Property(f => f.ActualAmount).IsRequired().HasPrecision(18, 2); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Instrument.cs b/PostFundManagement.Domain/Entities/Instrument.cs index fcda49e..d4bd7ae 100644 --- a/PostFundManagement.Domain/Entities/Instrument.cs +++ b/PostFundManagement.Domain/Entities/Instrument.cs @@ -1,9 +1,41 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Instrument : Models.Instrument { public virtual User? Creator { get; set; } public virtual Programme? Programme { get; set; } -} \ No newline at end of file +} + +public sealed class InstrumentConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Instrument).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProgrammeId).IsRequired(); + builder.Property(f => f.Code).IsRequired().HasMaxLength(ShortLabelLength); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("2") + .HasSentinel(ActivityStatus.Inactive); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + + builder.HasOne(f => f.Programme) + .WithMany(f => f.Instruments) + .HasForeignKey(fk => fk.ProgrammeId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } +} diff --git a/PostFundManagement.Domain/Entities/Invite.cs b/PostFundManagement.Domain/Entities/Invite.cs index 298290f..07e3cba 100644 --- a/PostFundManagement.Domain/Entities/Invite.cs +++ b/PostFundManagement.Domain/Entities/Invite.cs @@ -1,6 +1,6 @@ namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Invite : Models.Invite { public virtual Organisation? Organisation { get; set; } @@ -12,4 +12,53 @@ public class Invite : Models.Invite public virtual Contract? Contract { get; set; } public virtual User? RecipientUser { get; set; } +} + +public sealed class InviteConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Invite).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.OrganisationId).IsRequired(); + builder.Property(f => f.InvitedOrganisationId).IsRequired(); + builder.Property(f => f.AwardId).IsRequired(); + builder.Property(f => f.ContractId).IsRequired(); + builder.Property(f => f.Recipient).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + + builder.HasOne(f => f.Organisation) + .WithMany() + .HasForeignKey(fk => fk.OrganisationId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.InvitedOrganisation) + .WithMany() + .HasForeignKey(fk => fk.InvitedOrganisationId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Award) + .WithMany() + .HasForeignKey(fk => fk.AwardId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Contract) + .WithMany() + .HasForeignKey(fk => fk.ContractId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.RecipientUser) + .WithMany() + .HasForeignKey(fk => fk.Recipient) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Issue.cs b/PostFundManagement.Domain/Entities/Issue.cs index dac7511..b4e8642 100644 --- a/PostFundManagement.Domain/Entities/Issue.cs +++ b/PostFundManagement.Domain/Entities/Issue.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Issue : Models.Issue { public virtual Project? Project { get; set; } @@ -8,4 +10,44 @@ public class Issue : Models.Issue public virtual User? Creator { get; set; } public virtual User? Assignee { get; set; } +} + +public sealed class IssueConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Issue).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.ResolvedAt).IsRequired(false); + builder.Property(f => f.AssignedTo).IsRequired(false); + builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); + builder.Property(f => f.Priority).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(Priority.Low); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ActivityStatus.Active); + builder.Property(f => f.Resolution).IsRequired(); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Assignee) + .WithMany() + .HasForeignKey(fk => fk.AssignedTo) + .IsRequired() + .OnDelete(DeleteBehavior.SetNull); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Milestone.cs b/PostFundManagement.Domain/Entities/Milestone.cs index bffad70..693f342 100644 --- a/PostFundManagement.Domain/Entities/Milestone.cs +++ b/PostFundManagement.Domain/Entities/Milestone.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Milestone : Models.Milestone { public virtual Project? Project { get; set; } @@ -8,4 +10,43 @@ public class Milestone : Models.Milestone public virtual User? Creator { get; set; } public virtual User? Updator { get; set; } +} + +public sealed class MilestoneConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Milestone).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.DueAt).IsRequired(); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + builder.Property(f => f.Priority).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(Priority.Low); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Notification.cs b/PostFundManagement.Domain/Entities/Notification.cs index d85b6b8..ce256f9 100644 --- a/PostFundManagement.Domain/Entities/Notification.cs +++ b/PostFundManagement.Domain/Entities/Notification.cs @@ -1,9 +1,46 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Notification : Models.Notification { public virtual Organisation? Organisation { get; set; } public virtual User? RecipientUser { get; set; } +} + +public sealed class NotificationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Notification).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.SentAt).IsRequired(false); + builder.Property(f => f.Recipient).IsRequired(); + builder.Property(f => f.OrganisationId).IsRequired(); + builder.Property(f => f.Platform).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(NotificationPlatform.Email); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(NotificationStatus.Pending); + builder.Property(f => f.Subject).IsRequired(false).HasMaxLength(LabelLength); + builder.Property(f => f.Message).IsRequired().HasMaxLength(LargeTextLength); + builder.Property(f => f.Destination).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Error).IsRequired(false); + + builder.HasOne(f => f.Organisation) + .WithMany() + .HasForeignKey(fk => fk.OrganisationId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.RecipientUser) + .WithMany() + .HasForeignKey(fk => fk.Recipient) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Organisation.cs b/PostFundManagement.Domain/Entities/Organisation.cs index 3c0ffbc..eb8a2ef 100644 --- a/PostFundManagement.Domain/Entities/Organisation.cs +++ b/PostFundManagement.Domain/Entities/Organisation.cs @@ -1,7 +1,34 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Organisation : Models.Organisation { public virtual User? Creator { get; set; } +} + +public sealed class OrganisationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Organisation).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.RegistrationNo).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Email).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Type).IsRequired().HasConversion().HasDefaultValueSql("5") + .HasSentinel(OrganisationType.PrivateLimitedCompany); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ActivityStatus.Active); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Outcome.cs b/PostFundManagement.Domain/Entities/Outcome.cs index 7e29e82..dc6f43b 100644 --- a/PostFundManagement.Domain/Entities/Outcome.cs +++ b/PostFundManagement.Domain/Entities/Outcome.cs @@ -1,9 +1,38 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Outcome : Models.Outcome { public virtual Beneficiary? Beneficiary { get; set; } public virtual User? Creator { get; set; } +} + +public sealed class OutcomeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Outcome).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.BeneficiaryId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); + + builder.HasOne(f => f.Beneficiary) + .WithMany() + .HasForeignKey(fk => fk.BeneficiaryId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Portfolio.cs b/PostFundManagement.Domain/Entities/Portfolio.cs index 9117a5c..9b48583 100644 --- a/PostFundManagement.Domain/Entities/Portfolio.cs +++ b/PostFundManagement.Domain/Entities/Portfolio.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Portfolio : Models.Portfolio { public virtual User? Creator { get; set; } @@ -11,3 +13,37 @@ public class Portfolio : Models.Portfolio public virtual ICollection Programmes { get; set; } = []; } + +public sealed class PortfolioConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Portfolio).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.OrganisationId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.OwnedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength); + + builder.HasOne(f => f.Organisation) + .WithMany() + .HasForeignKey(fk => fk.OrganisationId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Owner) + .WithMany() + .HasForeignKey(fk => fk.OwnedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } +} \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Programme.cs b/PostFundManagement.Domain/Entities/Programme.cs index aa199db..052a261 100644 --- a/PostFundManagement.Domain/Entities/Programme.cs +++ b/PostFundManagement.Domain/Entities/Programme.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Programme : Models.Programme { public virtual User? Creator { get; set; } @@ -16,4 +18,41 @@ public class Programme : Models.Programme public virtual ICollection Projects { get; set; } = []; public virtual ICollection Awards { get; set; } = []; +} + +public sealed class ProgrammeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Programme).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.PortfolioId).IsRequired(); + builder.Property(f => f.OwnedBy).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + + builder.HasOne(f => f.Portfolio) + .WithMany(f => f.Programmes) + .HasForeignKey(fk => fk.PortfolioId) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Owner) + .WithMany() + .IsRequired() + .HasForeignKey(fk => fk.OwnedBy) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Project.cs b/PostFundManagement.Domain/Entities/Project.cs index 5840cf0..3bbfa25 100644 --- a/PostFundManagement.Domain/Entities/Project.cs +++ b/PostFundManagement.Domain/Entities/Project.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Project : Models.Project { public virtual User? Creator { get; set; } @@ -10,4 +12,47 @@ public class Project : Models.Project public virtual Programme? Programme { get; set; } public virtual ICollection Awards { get; set; } = []; +} + +public sealed class ProjectConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Project).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProgrammeId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.StartedAt).IsRequired(false); + builder.Property(f => f.EndedAt).IsRequired(false); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); + builder.Property(f => f.Sector).IsRequired().HasConversion().HasDefaultValueSql("11") + .HasSentinel(IndustrySector.InformationCommunicationTechnology); + builder.Property(f => f.ProjectType).IsRequired().HasConversion().HasDefaultValueSql("10") + .HasSentinel(ProjectType.Innovation); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + + builder.HasOne(f => f.Programme) + .WithMany(f => f.Projects) + .HasForeignKey(fk => fk.ProgrammeId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Risk.cs b/PostFundManagement.Domain/Entities/Risk.cs index 0750956..9686fa9 100644 --- a/PostFundManagement.Domain/Entities/Risk.cs +++ b/PostFundManagement.Domain/Entities/Risk.cs @@ -1,9 +1,42 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Risk : Models.Risk { public virtual Project? Project { get; set; } public virtual User? Creator { get; set; } +} + +public sealed class RiskConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Risk).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.Likelihood).IsRequired().HasConversion().HasDefaultValueSql("3") + .HasSentinel(RiskLikelihood.Possible); + builder.Property(f => f.Impact).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(RiskImpact.Negligible); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Rule.cs b/PostFundManagement.Domain/Entities/Rule.cs index ed1c9e6..cac2969 100644 --- a/PostFundManagement.Domain/Entities/Rule.cs +++ b/PostFundManagement.Domain/Entities/Rule.cs @@ -1,6 +1,8 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class Rule : Models.Rule { public virtual User? Creator { get; set; } @@ -8,4 +10,41 @@ public class Rule : Models.Rule public virtual User? Updator { get; set; } public virtual Programme? Programme { get; set; } +} + +public sealed class RuleConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Rule).Pluralize()); + + builder.HasKey(fk => fk.Id); + builder.Property(f => f.ProgrammeId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Version).IsRequired().HasDefaultValue(1); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("2") + .HasSentinel(ActivityStatus.Inactive); + + builder.HasOne(f => f.Programme) + .WithMany(f => f.Rules) + .HasForeignKey(fk => fk.ProgrammeId) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/SiteVisit.cs b/PostFundManagement.Domain/Entities/SiteVisit.cs index 3efab66..9d46ce9 100644 --- a/PostFundManagement.Domain/Entities/SiteVisit.cs +++ b/PostFundManagement.Domain/Entities/SiteVisit.cs @@ -1,9 +1,41 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] +[EntityTypeConfiguration] public class SiteVisit : Models.SiteVisit { public virtual Project? Project { get; set; } public virtual User? Creator { get; set; } +} + +public sealed class SiteVisitConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(SiteVisit).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.ProjectId).IsRequired(); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.VisitedAt).IsRequired(); + builder.Property(f => f.InspectorName).IsRequired(); + builder.Property(f => f.Findings).IsRequired(false).HasMaxLength(LargeTextLength); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ApprovalStatus.Pending); + + builder.HasOne(f => f.Project) + .WithMany() + .HasForeignKey(fk => fk.ProjectId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + } } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/Tenant.cs b/PostFundManagement.Domain/Entities/Tenant.cs new file mode 100644 index 0000000..6bab5aa --- /dev/null +++ b/PostFundManagement.Domain/Entities/Tenant.cs @@ -0,0 +1,40 @@ +using static PostFundManagement.Domain.Extensions.Constants; + +namespace PostFundManagement.Domain.Entities; + +[EntityTypeConfiguration] +public class Tenant : Models.Tenant +{ + public virtual User? Creator { get; set; } + + public virtual User? Updator { get; set; } +} + +public sealed class TenantConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Tenant).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UpdatedBy).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ActivityStatus.Active); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Updator) + .WithMany() + .HasForeignKey(fk => fk.UpdatedBy) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); + } +} \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/TenantOrganisation.cs b/PostFundManagement.Domain/Entities/TenantOrganisation.cs new file mode 100644 index 0000000..bf3cc15 --- /dev/null +++ b/PostFundManagement.Domain/Entities/TenantOrganisation.cs @@ -0,0 +1,45 @@ +namespace PostFundManagement.Domain.Entities; + +[EntityTypeConfiguration] +public class TenantOrganisation : Models.TenantOrganisation +{ + public virtual Tenant? Tenant { get; set; } + + public virtual Organisation? Organisation { get; set; } + + public virtual User? Creator { get; set; } +} + +public sealed class TenantOrganisationConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(TenantOrganisation).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.TenantId).IsRequired(); + builder.Property(f => f.OrganisationId).IsRequired(); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ActivityStatus.Active); + + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Organisation) + .WithMany() + .HasForeignKey(fk => fk.OrganisationId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + } +} diff --git a/PostFundManagement.Domain/Entities/TenantUser.cs b/PostFundManagement.Domain/Entities/TenantUser.cs new file mode 100644 index 0000000..cca0418 --- /dev/null +++ b/PostFundManagement.Domain/Entities/TenantUser.cs @@ -0,0 +1,39 @@ +namespace PostFundManagement.Domain.Entities; + +[EntityTypeConfiguration] +public class TenantUser : Models.TenantUser +{ + public virtual Tenant? Tenant { get; set; } + + public virtual User? Creator { get; set; } + + public virtual User? User { get; set; } +} + +public sealed class TenantUserConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(TenantUser).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()"); + builder.Property(f => f.CreatedBy).IsRequired(); + builder.Property(f => f.UserId).IsRequired(); + builder.Property(f => f.TenantId).IsRequired(); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") + .HasSentinel(ActivityStatus.Active); + + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(f => f.Creator) + .WithMany() + .HasForeignKey(fk => fk.CreatedBy) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + } +} diff --git a/PostFundManagement.Domain/Entities/User.cs b/PostFundManagement.Domain/Entities/User.cs index 19633b8..da7a74e 100644 --- a/PostFundManagement.Domain/Entities/User.cs +++ b/PostFundManagement.Domain/Entities/User.cs @@ -1,4 +1,20 @@ +using static PostFundManagement.Domain.Extensions.Constants; + namespace PostFundManagement.Domain.Entities; -[EntityTypeConfiguration] -public sealed class User : Models.User; \ No newline at end of file +[EntityTypeConfiguration] +public sealed class User : Models.User; + +public sealed class UserConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(User).Pluralize()); + + builder.HasKey(pk => pk.Id); + builder.Property(f => f.Email).IsRequired().HasMaxLength(LabelLength); + builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("2") + .HasSentinel(ActivityStatus.Inactive); + builder.Property(f => f.LastLoginAt).IsRequired(false); + } +} \ No newline at end of file diff --git a/PostFundManagement.Domain/Models/Tenant.cs b/PostFundManagement.Domain/Models/Tenant.cs new file mode 100644 index 0000000..d46ad75 --- /dev/null +++ b/PostFundManagement.Domain/Models/Tenant.cs @@ -0,0 +1,18 @@ +namespace PostFundManagement.Domain.Models; + +public class Tenant +{ + public long Id { get; set; } + + public DateTime CreatedAt { get; set; } + + public Guid CreatedBy { get; set; } + + public DateTime UpdatedAt { get; set; } + + public Guid UpdatedBy { get; set; } + + public string? Name { get; set; } + + public ActivityStatus Status { get; set; } +} diff --git a/PostFundManagement.Domain/Models/TenantOrganisation.cs b/PostFundManagement.Domain/Models/TenantOrganisation.cs new file mode 100644 index 0000000..5e1e17f --- /dev/null +++ b/PostFundManagement.Domain/Models/TenantOrganisation.cs @@ -0,0 +1,16 @@ +namespace PostFundManagement.Domain.Models; + +public class TenantOrganisation +{ + public long Id { get; set; } + + public DateTime CreatedAt { get; set; } + + public Guid CreatedBy { get; set; } + + public long TenantId { get; set; } + + public long OrganisationId { get; set; } + + public ActivityStatus Status { get; set; } +} diff --git a/PostFundManagement.Domain/Models/TenantUser.cs b/PostFundManagement.Domain/Models/TenantUser.cs new file mode 100644 index 0000000..c9d5911 --- /dev/null +++ b/PostFundManagement.Domain/Models/TenantUser.cs @@ -0,0 +1,16 @@ +namespace PostFundManagement.Domain.Models; + +public class TenantUser +{ + public long Id { get; set; } + + public DateTime CreatedAt { get; set; } + + public Guid CreatedBy { get; set; } + + public long TenantId { get; set; } + + public Guid UserId { get; set; } + + public ActivityStatus Status { get; set; } +} diff --git a/PostFundManagement.Domain/PostFundManagement.Domain.csproj b/PostFundManagement.Domain/PostFundManagement.Domain.csproj index b176c43..52e4769 100644 --- a/PostFundManagement.Domain/PostFundManagement.Domain.csproj +++ b/PostFundManagement.Domain/PostFundManagement.Domain.csproj @@ -9,6 +9,14 @@ ..\LICENSE + + 1701;1702;MA0048 + + + + 1701;1702;MA0048 + + diff --git a/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs b/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs index 0f4a87b..27b8776 100644 --- a/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs +++ b/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs @@ -4,6 +4,12 @@ namespace PostFundManagement.Infrastructure.Database; public sealed class ApplicationDbContext(DbContextOptions options) : DbContext(options) { + public DbSet TenantUsers => Set(); + + public DbSet TenantOrganisations => Set(); + + public DbSet Tenants => Set(); + public DbSet Notifications => Set(); public DbSet Invites => Set(); From f4024d563aa852c98d2fbcdc1e48a54dd09fee1d Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 21 Sep 2026 16:32:56 +0200 Subject: [PATCH 2/4] Implemented centralised audit logging --- PostFundManagement.Api/Program.cs | 3 + .../Services/ICommunicationsService.cs | 2 + .../Abstractions/Services/IEvidenceService.cs | 6 + .../Services/IExecutionService.cs | 6 + .../Services/IGovernanceService.cs | 9 +- .../Abstractions/Services/IGrantsService.cs | 11 +- .../Abstractions/Services/IIdentityService.cs | 5 +- .../Abstractions/Services/IProjectService.cs | 8 + .../Abstractions/Services/IUserService.cs | 3 + .../Extensions/Services.cs | 73 +- .../Filters/ActiveUserFilter.cs | 2 +- .../Interceptors/AuditInterceptor.cs | 211 ++ .../PostFundManagement.Application.csproj | 2 + .../Services/Shared/AuditService.cs | 27 + .../Abstractions/ITenantContextService.cs | 9 + .../Attributes/AuditAttribute.cs | 4 + .../Entities/AuditLog.cs | 17 +- PostFundManagement.Domain/Entities/Award.cs | 9 + .../Entities/Notification.cs | 9 + .../Entities/Portfolio.cs | 9 + .../Entities/Programme.cs | 9 + PostFundManagement.Domain/Entities/Project.cs | 9 + .../Entities/TenantUser.cs | 6 + .../Extensions/Mappers.cs | 40 +- PostFundManagement.Domain/Models/AuditLog.cs | 4 +- PostFundManagement.Domain/Models/Award.cs | 2 + .../Models/Notification.cs | 2 + PostFundManagement.Domain/Models/Portfolio.cs | 2 + PostFundManagement.Domain/Models/Programme.cs | 2 + PostFundManagement.Domain/Models/Project.cs | 2 + PostFundManagement.Domain/Models/Tenant.cs | 4 +- .../Database/ApplicationDbContext.cs | 55 +- .../Factories/ApplicationDbContextFactory.cs | 13 +- .../20260921085528_AddedTenant.Designer.cs | 2233 ++++++++++++++++ .../Migrations/20260921085528_AddedTenant.cs | 370 +++ .../20260921125152_UpdateAuditLog.Designer.cs | 2246 +++++++++++++++++ .../20260921125152_UpdateAuditLog.cs | 64 + .../ApplicationDbContextModelSnapshot.cs | 273 +- .../DesignTimeTenantContextService.cs | 14 + .../Database/Services/TenantContextService.cs | 29 + .../PostFundManagement.Infrastructure.csproj | 1 + 41 files changed, 5751 insertions(+), 54 deletions(-) create mode 100644 PostFundManagement.Application/Interceptors/AuditInterceptor.cs create mode 100644 PostFundManagement.Application/Services/Shared/AuditService.cs create mode 100644 PostFundManagement.Domain/Abstractions/ITenantContextService.cs create mode 100644 PostFundManagement.Domain/Attributes/AuditAttribute.cs create mode 100644 PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.Designer.cs create mode 100644 PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.cs create mode 100644 PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.Designer.cs create mode 100644 PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.cs create mode 100644 PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs create mode 100644 PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs diff --git a/PostFundManagement.Api/Program.cs b/PostFundManagement.Api/Program.cs index 2d378b6..ee03d59 100644 --- a/PostFundManagement.Api/Program.cs +++ b/PostFundManagement.Api/Program.cs @@ -1,8 +1,10 @@ using Microsoft.FeatureManagement; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Filters; +using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Health; using PostFundManagement.Domain.Mediator; +using PostFundManagement.Infrastructure.Database.Services; using static PostFundManagement.Domain.Extensions.Constants; var builder = WebApplication.CreateBuilder(args); @@ -27,6 +29,7 @@ builder.Services.AddApiVersioning(options => }); builder.Services.AddScoped(); +builder.Services.AddTransient(); builder.Services.AddEndpoints(Assembly.GetExecutingAssembly()); builder.Services.AddApiServices(builder.Configuration); diff --git a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs index 9a3b259..10aebbb 100644 --- a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs +++ b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs @@ -1,11 +1,13 @@ using PostFundManagement.Application.Services.Communications; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface ICommunicationsService { + [Audit] ValueTask> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs b/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs index 57550a0..751e11b 100644 --- a/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs @@ -1,21 +1,26 @@ using PostFundManagement.Application.Services.Evidence; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IEvidenceService { + [Audit] ValueTask CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default); + [Audit] ValueTask RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default); ValueTask> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken); @@ -26,6 +31,7 @@ public interface IEvidenceService ValueTask> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default); + [Audit] ValueTask UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs b/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs index df89145..6cd37bc 100644 --- a/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs @@ -1,5 +1,6 @@ using PostFundManagement.Application.Services.Execution; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; @@ -10,17 +11,22 @@ public interface IExecutionService ValueTask> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default); ValueTask> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default); + [Audit] ValueTask UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default); + [Audit] ValueTask UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs index c1d7b61..477125e 100644 --- a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs @@ -1,5 +1,6 @@ using PostFundManagement.Application.Services.Governance; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; @@ -18,12 +19,16 @@ public interface IGovernanceService ValueTask> GetDashboardAsync(CancellationToken cancellationToken = default); + [Audit] ValueTask CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default); - + + [Audit] ValueTask CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); - + + [Audit] ValueTask UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs b/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs index ca9bf30..22dce48 100644 --- a/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs @@ -1,5 +1,6 @@ using PostFundManagement.Application.Services.Grants; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; @@ -18,17 +19,23 @@ public interface IGrantsService ValueTask> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default); + [Audit] ValueTask ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default); - + + [Audit] ValueTask UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default); - + + [Audit] ValueTask UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs b/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs index bb4036e..9056366 100644 --- a/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs @@ -1,6 +1,9 @@ -namespace PostFundManagement.Application.Abstractions.Services; +using PostFundManagement.Domain.Attributes; + +namespace PostFundManagement.Application.Abstractions.Services; public interface IIdentityService { + [Audit] ValueTask UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs index f99f26d..b5f80d5 100644 --- a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs @@ -1,11 +1,13 @@ using PostFundManagement.Application.Services.Projects; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IProjectService { + [Audit] ValueTask UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default); @@ -14,8 +16,10 @@ public interface IProjectService ValueTask> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default); @@ -26,16 +30,20 @@ public interface IProjectService ValueTask> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default); + [Audit] ValueTask UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default); ValueTask> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default); + [Audit] ValueTask CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] ValueTask UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); ValueTask> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IUserService.cs b/PostFundManagement.Application/Abstractions/Services/IUserService.cs index 3cd5bdc..9b6d5c3 100644 --- a/PostFundManagement.Application/Abstractions/Services/IUserService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IUserService.cs @@ -1,15 +1,18 @@ using PostFundManagement.Application.Services.Users; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IUserService { + [Audit] ValueTask SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default); ValueTask> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default); + [Audit] ValueTask UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default); ValueTask> GetUserAsync(Guid userId, CancellationToken cancellationToken); diff --git a/PostFundManagement.Application/Extensions/Services.cs b/PostFundManagement.Application/Extensions/Services.cs index c571328..127a317 100644 --- a/PostFundManagement.Application/Extensions/Services.cs +++ b/PostFundManagement.Application/Extensions/Services.cs @@ -1,5 +1,6 @@ using PostFundManagement.Application.Abstractions; using PostFundManagement.Application.Abstractions.Services; +using PostFundManagement.Application.Interceptors; using PostFundManagement.Application.Services.Communications; using PostFundManagement.Application.Services.Evidence; using PostFundManagement.Application.Services.Execution; @@ -15,32 +16,60 @@ namespace PostFundManagement.Application.Extensions; public static class Services { + public static IServiceCollection AddAuditedCachedScoped(this IServiceCollection services) + where TInterface : class + where TImplementation : class, TInterface + where TCacheDecorator : class, TInterface + { + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(sp => + { + var proxyGenerator = sp.GetRequiredService(); + var interceptor = sp.GetRequiredService(); + + var actual = sp.GetRequiredService(); + + var cached = ActivatorUtilities.CreateInstance(sp, actual); + + return proxyGenerator.CreateInterfaceProxyWithTarget(cached, interceptor.ToInterceptor()); + }); + + return services; + } + + public static IServiceCollection AddAuditedScoped(this IServiceCollection services) + where TInterface : class + where TImplementation : class, TInterface + { + services.AddScoped(); + services.AddScoped(sp => + { + var proxyGenerator = sp.GetRequiredService(); + var actual = sp.GetRequiredService(); + var interceptor = sp.GetRequiredService(); + + return proxyGenerator.CreateInterfaceProxyWithTarget(actual, interceptor.ToInterceptor()); + }); + + return services; + } + public static IServiceCollection AddRegisterServicesWithCaching(this IServiceCollection services, IConfiguration configuration) { - services.AddScoped(); - services.Decorate(); + services.AddScoped(); + services.AddSingleton(); - services.AddScoped(); - services.Decorate(); + services.AddAuditedCachedScoped(); + services.AddAuditedCachedScoped(); + services.AddAuditedCachedScoped(); + services.AddAuditedCachedScoped(); + services.AddAuditedCachedScoped(); + services.AddAuditedCachedScoped(); + services.AddAuditedCachedScoped(); + services.AddAuditedCachedScoped(); - services.AddScoped(); - services.Decorate(); - - services.AddScoped(); - services.Decorate(); - - services.AddScoped(); - services.Decorate(); - - services.AddScoped(); - services.Decorate(); - - services.AddScoped(); - services.Decorate(); - - services.AddScoped(); - services.Decorate(); - services.AddScoped(); services.Decorate(); diff --git a/PostFundManagement.Application/Filters/ActiveUserFilter.cs b/PostFundManagement.Application/Filters/ActiveUserFilter.cs index feba9b0..0bd8ecd 100644 --- a/PostFundManagement.Application/Filters/ActiveUserFilter.cs +++ b/PostFundManagement.Application/Filters/ActiveUserFilter.cs @@ -26,7 +26,7 @@ public sealed class ActiveUserFilter(IIdentityService identityService) : IEndpoi if (!string.IsNullOrWhiteSpace(path) && !path!.EndsWith("integration/sync", StringComparison.InvariantCultureIgnoreCase)) if ((await identityService.UserIsAuthorisedAsync(userId, context.HttpContext.RequestAborted)).IsFailed) - return (object?)Results.Json(new { message = "Account approval is pending or inactive." }, statusCode: StatusCodes.Status403Forbidden); + return Results.Json(new { message = "Account approval is pending or inactive." }, statusCode: StatusCodes.Status403Forbidden); context.HttpContext.Items[UserIdItemKey] = userId; context.HttpContext.Items[EmailtemKey] = email; diff --git a/PostFundManagement.Application/Interceptors/AuditInterceptor.cs b/PostFundManagement.Application/Interceptors/AuditInterceptor.cs new file mode 100644 index 0000000..02f2aab --- /dev/null +++ b/PostFundManagement.Application/Interceptors/AuditInterceptor.cs @@ -0,0 +1,211 @@ +using PostFundManagement.Application.Services.Shared; +using PostFundManagement.Domain.Attributes; + +namespace PostFundManagement.Application.Interceptors; + +public sealed class AuditInterceptor(AuditService auditService, IHttpContextAccessor httpContextAccessor) : IAsyncInterceptor +{ + public void InterceptSynchronous(IInvocation invocation) => invocation.Proceed(); + + public void InterceptAsynchronous(IInvocation invocation) => + invocation.ReturnValue = InternalInterceptAsynchronous(invocation); + + public void InterceptAsynchronous(IInvocation invocation) => + invocation.ReturnValue = InternalInterceptAsynchronous(invocation); + + private static async Task InternalInterceptAsynchronous(IInvocation invocation) + { + invocation.Proceed(); + + var task = (Task)invocation.ReturnValue; + + await task.ConfigureAwait(false); + } + + private async Task InternalInterceptAsynchronous(IInvocation invocation) + { + invocation.Proceed(); + + var task = (Task)invocation.ReturnValue; + var resultObj = await task.ConfigureAwait(false); + + var method = invocation.MethodInvocationTarget ?? invocation.Method; + var auditAttr = method.GetCustomAttribute() + ?? invocation.Method.GetCustomAttribute(); + + if (auditAttr == null) return resultObj; + + if (resultObj is IResultBase result && result.IsSuccess) + { + try + { + var action = ResolveActionName(method.Name); + var entityName = ResolveEntityName(method, resultObj, invocation.Arguments); + var entityId = ResolveEntityId(resultObj, invocation.Arguments); + var changes = BuildPayloadSummary(invocation.Method.GetParameters(), invocation.Arguments); + var performedBy = ResolveUserId(); + + await auditService.LogActionAsync( + entityId: entityId, + entityName: entityName, + action: action, + changes: changes, + performedBy: performedBy).ConfigureAwait(false); + } + catch { /* eat all exceptions, don't interrupt program flow */ } + } + + return resultObj; + } + + private static string ResolveActionName(string methodName) => + methodName.EndsWith("Async", StringComparison.Ordinal) + ? methodName[..^5] + : methodName; + + private static string ResolveEntityName(MethodInfo method, object? resultObj, object[] args) + { + var returnType = method.ReturnType; + + Type? innerType = null; + + if (returnType.IsGenericType) + { + var genericDef = returnType.GetGenericTypeDefinition(); + + if (genericDef == typeof(Task<>) || genericDef == typeof(ValueTask<>)) + innerType = returnType.GetGenericArguments()[0]; + } + + if (innerType != null && innerType.IsGenericType && typeof(IResultBase).IsAssignableFrom(innerType)) + { + var valueType = innerType.GetGenericArguments().FirstOrDefault(); + + if (valueType != null && valueType != typeof(bool) && valueType != typeof(int) && valueType != typeof(string)) + return valueType.Name; + } + + foreach (var arg in args) + { + if (arg == null || arg is CancellationToken or IFormFile) continue; + + var argTypeName = arg.GetType().Name; + + if (argTypeName.EndsWith("Request", StringComparison.Ordinal)) + { + var trimmed = argTypeName[..^7]; + + return trimmed.Replace("Create", "").Replace("Update", ""); + } + } + + return ResolveActionName(method.Name); + } + + private static string ResolveEntityId(object? resultObj, object[] args) + { + if (resultObj != null) + { + var valueProp = resultObj.GetType().GetProperty("Value"); + if (valueProp != null) + { + var value = valueProp.GetValue(resultObj); + if (value != null) + { + var idProp = value.GetType().GetProperty("Id") ?? value.GetType().GetProperty("id"); + if (idProp != null) + { + var idVal = idProp.GetValue(value); + if (idVal != null) return idVal.ToString()!; + } + } + } + } + + foreach (var arg in args) + { + if (arg is long or int or Guid or string) + return arg.ToString()!; + + if (arg != null && arg is not CancellationToken and not IFormFile) + { + var idProp = arg.GetType().GetProperty("Id") ?? arg.GetType().GetProperty("id"); + + if (idProp != null) + { + var idVal = idProp.GetValue(arg); + + if (idVal != null) return idVal.ToString()!; + } + } + } + + return "N/A"; + } + + private static string[] BuildPayloadSummary(ParameterInfo[] parameters, object[] args) + { + var entries = new List(); + + for (var i = 0; i < parameters.Length; i++) + { + var param = parameters[i]; + var arg = args[i]; + + if (param.ParameterType == typeof(CancellationToken)) + continue; + + if (arg == null) + { + entries.Add($"{param.Name}: null"); + + continue; + } + + if (arg is IFormFile file) + { + entries.Add($"{param.Name}: [File] Name='{file.FileName}', Size={file.Length} bytes, Type='{file.ContentType}'"); + + continue; + } + + if (arg is IEnumerable files) + { + var fileSummaries = files.Select(f => $"'{f.FileName}' ({f.Length} bytes)"); + + entries.Add($"{param.Name}: [Files] count={fileSummaries.Count()} [{string.Join(", ", fileSummaries)}]"); + + continue; + } + + if (arg is Stream stream) + { + entries.Add($"{param.Name}: [Stream] Length={(stream.CanSeek ? stream.Length : -1)} bytes"); + + continue; + } + + try + { + var json = JsonSerializer.Serialize(arg); + + entries.Add($"{param.Name}: {json}"); + } + catch + { + entries.Add($"{param.Name}: [Unserializable]"); + } + } + + return [.. entries]; + } + + private Guid ResolveUserId() + { + var user = httpContextAccessor.HttpContext?.User; + + var sub = user?.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? user?.FindFirst("sub")?.Value; + + return Guid.TryParse(sub, out var guid) ? guid : Guid.Empty; + } +} \ No newline at end of file diff --git a/PostFundManagement.Application/PostFundManagement.Application.csproj b/PostFundManagement.Application/PostFundManagement.Application.csproj index b69904b..cb7e900 100644 --- a/PostFundManagement.Application/PostFundManagement.Application.csproj +++ b/PostFundManagement.Application/PostFundManagement.Application.csproj @@ -24,6 +24,7 @@ + @@ -36,6 +37,7 @@ + diff --git a/PostFundManagement.Application/Services/Shared/AuditService.cs b/PostFundManagement.Application/Services/Shared/AuditService.cs new file mode 100644 index 0000000..5104493 --- /dev/null +++ b/PostFundManagement.Application/Services/Shared/AuditService.cs @@ -0,0 +1,27 @@ +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Domain.Errors; +using PostFundManagement.Infrastructure.Database; + +namespace PostFundManagement.Application.Services.Shared; + +public sealed class AuditService(IDbContextFactory contextFactory, ITenantContextService tenantContextService) : IService +{ + public async ValueTask LogActionAsync(string entityId, string entityName, string action, string[] changes, Guid performedBy, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + context.Add(new Domain.Entities.AuditLog + { + EntityId = entityId, + Action = action, + Changes = changes, + EntityName = entityName, + PerformedBy = performedBy, + Timestamp = DateTime.UtcNow, + TenantId = tenantContextService.TenantId > 0 ? tenantContextService.TenantId : null, + }); + + return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New); + })); +} diff --git a/PostFundManagement.Domain/Abstractions/ITenantContextService.cs b/PostFundManagement.Domain/Abstractions/ITenantContextService.cs new file mode 100644 index 0000000..21a94cc --- /dev/null +++ b/PostFundManagement.Domain/Abstractions/ITenantContextService.cs @@ -0,0 +1,9 @@ +namespace PostFundManagement.Domain.Abstractions; + +public interface ITenantContextService +{ + string? TenantName { get; } + long TenantId { get; } + bool IsAdmin { get; } + bool HasTenantFilter { get; } +} diff --git a/PostFundManagement.Domain/Attributes/AuditAttribute.cs b/PostFundManagement.Domain/Attributes/AuditAttribute.cs new file mode 100644 index 0000000..7d5d758 --- /dev/null +++ b/PostFundManagement.Domain/Attributes/AuditAttribute.cs @@ -0,0 +1,4 @@ +namespace PostFundManagement.Domain.Attributes; + +[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] +public sealed class AuditAttribute : Attribute; diff --git a/PostFundManagement.Domain/Entities/AuditLog.cs b/PostFundManagement.Domain/Entities/AuditLog.cs index 53cf177..7d8d833 100644 --- a/PostFundManagement.Domain/Entities/AuditLog.cs +++ b/PostFundManagement.Domain/Entities/AuditLog.cs @@ -5,16 +5,19 @@ namespace PostFundManagement.Domain.Entities; [EntityTypeConfiguration] public class AuditLog : Models.AuditLog { - public virtual User? Performer { get; set; } + public virtual Tenant? Tenant { get; set; } + + public virtual User? Performer { get; set; } } -public sealed class AuditLogConfiguration : IEntityTypeConfiguration +public sealed class AuditLogConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Domain.Entities.AuditLog).Pluralize()); + builder.ToTable(nameof(AuditLog).Pluralize()); builder.HasKey(pk => pk.Id); + builder.Property(f => f.TenantId).IsRequired(false); builder.Property(f => f.EntityName).IsRequired().HasMaxLength(LabelLength); builder.Property(f => f.EntityId).IsRequired(); builder.Property(f => f.Action).IsRequired().HasMaxLength(ShortLabelLength); @@ -22,6 +25,12 @@ public sealed class AuditLogConfiguration : IEntityTypeConfiguration f.PerformedBy).IsRequired(); builder.Property(f => f.Timestamp).IsRequired().HasDefaultValueSql("now()"); + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired(false) + .OnDelete(DeleteBehavior.NoAction); + builder.HasOne(f => f.Performer) .WithMany() .HasForeignKey(fk => fk.PerformedBy) diff --git a/PostFundManagement.Domain/Entities/Award.cs b/PostFundManagement.Domain/Entities/Award.cs index 488d234..cfaf78c 100644 --- a/PostFundManagement.Domain/Entities/Award.cs +++ b/PostFundManagement.Domain/Entities/Award.cs @@ -9,6 +9,8 @@ public class Award : Models.Award public virtual User? Approver { get; set; } + public virtual Tenant? Tenant { get; set; } + public virtual Organisation? Organisation { get; set; } public virtual Programme? Programme { get; set; } @@ -27,6 +29,7 @@ public sealed class AwardConfiguration : IEntityTypeConfiguration builder.HasKey(pk => pk.Id); builder.Property(f => f.OrganisationId).IsRequired(); builder.Property(f => f.ProgrammeId).IsRequired(); + builder.Property(f => f.TenantId).IsRequired(); builder.Property(f => f.ProjectId).IsRequired(false); builder.Property(f => f.CreatedBy).IsRequired(); builder.Property(f => f.UpdatedBy).IsRequired(false); @@ -37,6 +40,12 @@ public sealed class AwardConfiguration : IEntityTypeConfiguration .HasSentinel(ApprovalStatus.Pending); builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(f => f.Organisation) .WithMany() .HasForeignKey(fk => fk.OrganisationId) diff --git a/PostFundManagement.Domain/Entities/Notification.cs b/PostFundManagement.Domain/Entities/Notification.cs index ce256f9..d6cfc05 100644 --- a/PostFundManagement.Domain/Entities/Notification.cs +++ b/PostFundManagement.Domain/Entities/Notification.cs @@ -5,6 +5,8 @@ namespace PostFundManagement.Domain.Entities; [EntityTypeConfiguration] public class Notification : Models.Notification { + public virtual Tenant? Tenant { get; set; } + public virtual Organisation? Organisation { get; set; } public virtual User? RecipientUser { get; set; } @@ -22,6 +24,7 @@ public sealed class NotificationConfiguration : IEntityTypeConfiguration f.SentAt).IsRequired(false); builder.Property(f => f.Recipient).IsRequired(); builder.Property(f => f.OrganisationId).IsRequired(); + builder.Property(f => f.TenantId).IsRequired(); builder.Property(f => f.Platform).IsRequired().HasConversion().HasDefaultValueSql("1") .HasSentinel(NotificationPlatform.Email); builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") @@ -31,6 +34,12 @@ public sealed class NotificationConfiguration : IEntityTypeConfiguration f.Destination).IsRequired().HasMaxLength(LabelLength); builder.Property(f => f.Error).IsRequired(false); + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(f => f.Organisation) .WithMany() .HasForeignKey(fk => fk.OrganisationId) diff --git a/PostFundManagement.Domain/Entities/Portfolio.cs b/PostFundManagement.Domain/Entities/Portfolio.cs index 9b48583..7c2ff85 100644 --- a/PostFundManagement.Domain/Entities/Portfolio.cs +++ b/PostFundManagement.Domain/Entities/Portfolio.cs @@ -9,6 +9,8 @@ public class Portfolio : Models.Portfolio public virtual User? Owner { get; set; } + public virtual Tenant? Tenant { get; set; } + public virtual Organisation? Organisation { get; set; } public virtual ICollection Programmes { get; set; } = []; @@ -21,6 +23,7 @@ public sealed class PortfolioConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(Portfolio).Pluralize()); builder.HasKey(pk => pk.Id); + builder.Property(f => f.TenantId).IsRequired(); builder.Property(f => f.OrganisationId).IsRequired(); builder.Property(f => f.CreatedBy).IsRequired(); builder.Property(f => f.OwnedBy).IsRequired(); @@ -28,6 +31,12 @@ public sealed class PortfolioConfiguration : IEntityTypeConfiguration builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength); builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength); + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(f => f.Organisation) .WithMany() .HasForeignKey(fk => fk.OrganisationId) diff --git a/PostFundManagement.Domain/Entities/Programme.cs b/PostFundManagement.Domain/Entities/Programme.cs index 052a261..4ce3583 100644 --- a/PostFundManagement.Domain/Entities/Programme.cs +++ b/PostFundManagement.Domain/Entities/Programme.cs @@ -8,6 +8,8 @@ public class Programme : Models.Programme public virtual User? Creator { get; set; } public virtual User? Owner { get; set; } + + public virtual Tenant? Tenant { get; set; } public virtual Portfolio? Portfolio { get; set; } @@ -27,6 +29,7 @@ public sealed class ProgrammeConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(Programme).Pluralize()); builder.HasKey(pk => pk.Id); + builder.Property(f => f.TenantId).IsRequired(); builder.Property(f => f.PortfolioId).IsRequired(); builder.Property(f => f.OwnedBy).IsRequired(); builder.Property(f => f.CreatedBy).IsRequired(); @@ -37,6 +40,12 @@ public sealed class ProgrammeConfiguration : IEntityTypeConfiguration builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") .HasSentinel(ApprovalStatus.Pending); + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired() + .OnDelete(DeleteBehavior.NoAction); + builder.HasOne(f => f.Portfolio) .WithMany(f => f.Programmes) .HasForeignKey(fk => fk.PortfolioId) diff --git a/PostFundManagement.Domain/Entities/Project.cs b/PostFundManagement.Domain/Entities/Project.cs index 3bbfa25..37be0b8 100644 --- a/PostFundManagement.Domain/Entities/Project.cs +++ b/PostFundManagement.Domain/Entities/Project.cs @@ -9,6 +9,8 @@ public class Project : Models.Project public virtual User? Updator { get; set; } + public virtual Tenant? Tenant { get; set; } + public virtual Programme? Programme { get; set; } public virtual ICollection Awards { get; set; } = []; @@ -21,6 +23,7 @@ public sealed class ProjectConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(Project).Pluralize()); builder.HasKey(pk => pk.Id); + builder.Property(f => f.TenantId).IsRequired(); builder.Property(f => f.ProgrammeId).IsRequired(); builder.Property(f => f.CreatedBy).IsRequired(); builder.Property(f => f.UpdatedBy).IsRequired(false); @@ -37,6 +40,12 @@ public sealed class ProjectConfiguration : IEntityTypeConfiguration builder.Property(f => f.Status).IsRequired().HasConversion().HasDefaultValueSql("1") .HasSentinel(ApprovalStatus.Pending); + builder.HasOne(f => f.Tenant) + .WithMany() + .HasForeignKey(fk => fk.TenantId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(f => f.Programme) .WithMany(f => f.Projects) .HasForeignKey(fk => fk.ProgrammeId) diff --git a/PostFundManagement.Domain/Entities/TenantUser.cs b/PostFundManagement.Domain/Entities/TenantUser.cs index cca0418..0d9f570 100644 --- a/PostFundManagement.Domain/Entities/TenantUser.cs +++ b/PostFundManagement.Domain/Entities/TenantUser.cs @@ -30,6 +30,12 @@ public sealed class TenantUserConfiguration : IEntityTypeConfiguration f.User) + .WithMany() + .HasForeignKey(fk => fk.UserId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(f => f.Creator) .WithMany() .HasForeignKey(fk => fk.CreatedBy) diff --git a/PostFundManagement.Domain/Extensions/Mappers.cs b/PostFundManagement.Domain/Extensions/Mappers.cs index 1e547ff..0355088 100644 --- a/PostFundManagement.Domain/Extensions/Mappers.cs +++ b/PostFundManagement.Domain/Extensions/Mappers.cs @@ -4,6 +4,37 @@ namespace PostFundManagement.Domain.Extensions; public static class Mappers { + public static TenantUser Map(this Entities.TenantUser entity) => new() + { + Id = entity.Id, + TenantId = entity.TenantId, + CreatedAt = entity.CreatedAt, + CreatedBy = entity.CreatedBy, + Status = entity.Status, + UserId = entity.UserId + }; + + public static TenantOrganisation Map(this Entities.TenantOrganisation entity) => new() + { + Id = entity.Id, + Status = entity.Status, + CreatedAt = entity.CreatedAt, + CreatedBy = entity.CreatedBy, + OrganisationId = entity.OrganisationId, + TenantId = entity.TenantId + }; + + public static Tenant Map(this Entities.Tenant entity) => new() + { + Id = entity.Id, + Name = entity.Name, + CreatedAt = entity.CreatedAt, + CreatedBy = entity.CreatedBy, + Status = entity.Status, + UpdatedAt = entity.UpdatedAt, + UpdatedBy = entity.UpdatedBy + }; + public static Activity Map(this Entities.Activity entity) => new() { Id = entity.Id, @@ -40,6 +71,7 @@ public static class Mappers ApprovedBy = entity.ApprovedBy, CreatedAt = entity.CreatedAt, CreatedBy = entity.CreatedBy, + TenantId = entity.TenantId, OrganisationId = entity.OrganisationId, ProgrammeId = entity.ProgrammeId, ProjectId = entity.ProjectId, @@ -162,7 +194,7 @@ public static class Mappers AwardId = entity.AwardId, ContractId = entity.ContractId, CreatedAt = entity.CreatedAt, - CreatedBy = entity.CreatedBy, + CreatedBy = entity.CreatedBy, InvitedOrganisationId = entity.InvitedOrganisationId, OrganisationId = entity.OrganisationId, Recipient = entity.Recipient, @@ -202,7 +234,7 @@ public static class Mappers public static Organisation Map(this Entities.Organisation entity) => new() { Id = entity.Id, - CreatedAt = entity.CreatedAt, + CreatedAt = entity.CreatedAt, CreatedBy = entity.CreatedBy, Email = entity.Email, Name = entity.Name, @@ -225,6 +257,7 @@ public static class Mappers { Id = entity.Id, CreatedAt = entity.CreatedAt, + TenantId = entity.TenantId, CreatedBy = entity.CreatedBy, Description = entity.Description, Name = entity.Name, @@ -239,6 +272,7 @@ public static class Mappers Destination = entity.Destination, Error = entity.Error, Message = entity.Message, + TenantId = entity.TenantId, OrganisationId = entity.OrganisationId, Platform = entity.Platform, Recipient = entity.Recipient, @@ -255,6 +289,7 @@ public static class Mappers CreatedBy = entity.CreatedBy, Name = entity.Name, OwnedBy = entity.OwnedBy, + TenantId = entity.TenantId, PortfolioId = entity.PortfolioId, Status = entity.Status, UpdatedAt = entity.UpdatedAt, @@ -269,6 +304,7 @@ public static class Mappers Description = entity.Description, EndedAt = entity.EndedAt, Name = entity.Name, + TenantId = entity.TenantId, ProgrammeId = entity.ProgrammeId, StartedAt = entity.StartedAt, Status = entity.Status, diff --git a/PostFundManagement.Domain/Models/AuditLog.cs b/PostFundManagement.Domain/Models/AuditLog.cs index 91ed184..1bcd1a2 100644 --- a/PostFundManagement.Domain/Models/AuditLog.cs +++ b/PostFundManagement.Domain/Models/AuditLog.cs @@ -4,9 +4,11 @@ public class AuditLog { public long Id { get; set; } + public long? TenantId { get; set; } + public string? EntityName { get; set; } - public long EntityId { get; set; } + public string? EntityId { get; set; } public string? Action { get; set; } diff --git a/PostFundManagement.Domain/Models/Award.cs b/PostFundManagement.Domain/Models/Award.cs index cbb27e0..e77c763 100644 --- a/PostFundManagement.Domain/Models/Award.cs +++ b/PostFundManagement.Domain/Models/Award.cs @@ -6,6 +6,8 @@ public class Award public long OrganisationId { get; set; } + public long TenantId { get; set; } + public long ProgrammeId { get; set; } public long? ProjectId { get; set; } diff --git a/PostFundManagement.Domain/Models/Notification.cs b/PostFundManagement.Domain/Models/Notification.cs index affa356..ce678e6 100644 --- a/PostFundManagement.Domain/Models/Notification.cs +++ b/PostFundManagement.Domain/Models/Notification.cs @@ -4,6 +4,8 @@ public class Notification { public long Id { get; set; } + public long TenantId { get; set; } + public long OrganisationId { get; set; } public Guid Recipient { get; set; } diff --git a/PostFundManagement.Domain/Models/Portfolio.cs b/PostFundManagement.Domain/Models/Portfolio.cs index 23a04bb..3bd7f53 100644 --- a/PostFundManagement.Domain/Models/Portfolio.cs +++ b/PostFundManagement.Domain/Models/Portfolio.cs @@ -4,6 +4,8 @@ public class Portfolio { public long Id { get; set; } + public long TenantId { get; set; } + public long OrganisationId { get; set; } public Guid OwnedBy { get; set; } diff --git a/PostFundManagement.Domain/Models/Programme.cs b/PostFundManagement.Domain/Models/Programme.cs index e05fd15..e34a0e1 100644 --- a/PostFundManagement.Domain/Models/Programme.cs +++ b/PostFundManagement.Domain/Models/Programme.cs @@ -6,6 +6,8 @@ public class Programme public long PortfolioId { get; set; } + public long TenantId { get; set; } + public Guid OwnedBy { get; set; } public Guid CreatedBy { get; set; } diff --git a/PostFundManagement.Domain/Models/Project.cs b/PostFundManagement.Domain/Models/Project.cs index 795f75a..abcc9f9 100644 --- a/PostFundManagement.Domain/Models/Project.cs +++ b/PostFundManagement.Domain/Models/Project.cs @@ -6,6 +6,8 @@ public class Project public long ProgrammeId { get; set; } + public long TenantId { get; set; } + public Guid CreatedBy { get; set; } public Guid? UpdatedBy { get; set; } diff --git a/PostFundManagement.Domain/Models/Tenant.cs b/PostFundManagement.Domain/Models/Tenant.cs index d46ad75..792dfb9 100644 --- a/PostFundManagement.Domain/Models/Tenant.cs +++ b/PostFundManagement.Domain/Models/Tenant.cs @@ -8,9 +8,9 @@ public class Tenant public Guid CreatedBy { get; set; } - public DateTime UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } - public Guid UpdatedBy { get; set; } + public Guid? UpdatedBy { get; set; } public string? Name { get; set; } diff --git a/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs b/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs index 27b8776..df81852 100644 --- a/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs +++ b/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs @@ -1,9 +1,14 @@ +using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Entities; namespace PostFundManagement.Infrastructure.Database; -public sealed class ApplicationDbContext(DbContextOptions options) : DbContext(options) +public sealed class ApplicationDbContext(DbContextOptions options, + ITenantContextService tenantService) : DbContext(options) { + private bool BypassFilter => tenantService.IsAdmin; + private long CurrentTenantId => tenantService.TenantId; + public DbSet TenantUsers => Set(); public DbSet TenantOrganisations => Set(); @@ -17,13 +22,13 @@ public sealed class ApplicationDbContext(DbContextOptions public DbSet Activities => Set(); public DbSet AuditLogs => Set(); - + public DbSet SiteVisits => Set(); - + public DbSet ChangeRequests => Set(); public DbSet ComplianceItems => Set(); - + public DbSet Issues => Set(); public DbSet Outcomes => Set(); @@ -31,13 +36,13 @@ public sealed class ApplicationDbContext(DbContextOptions public DbSet Beneficiaries => Set(); public DbSet Evidences => Set(); - + public DbSet Risks => Set(); - + public DbSet Indicators => Set(); - + public DbSet Disbursements => Set(); - + public DbSet Milestones => Set(); public DbSet Budgets => Set(); @@ -57,6 +62,38 @@ public sealed class ApplicationDbContext(DbContextOptions public DbSet Portfolios => Set(); public DbSet Instruments => Set(); - + public DbSet Users => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity() + .HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + + modelBuilder.Entity().HasQueryFilter(o => BypassFilter || + Set().Any(to => to.TenantId == CurrentTenantId && to.OrganisationId == o.Id)); + } } \ No newline at end of file diff --git a/PostFundManagement.Infrastructure/Database/Factories/ApplicationDbContextFactory.cs b/PostFundManagement.Infrastructure/Database/Factories/ApplicationDbContextFactory.cs index 7d06651..6046775 100644 --- a/PostFundManagement.Infrastructure/Database/Factories/ApplicationDbContextFactory.cs +++ b/PostFundManagement.Infrastructure/Database/Factories/ApplicationDbContextFactory.cs @@ -2,7 +2,7 @@ using static PostFundManagement.Domain.Extensions.Constants; namespace PostFundManagement.Infrastructure.Database.Factories; -public sealed class ApplicationDbContextFactory : IDesignTimeDbContextFactory +public sealed partial class ApplicationDbContextFactory : IDesignTimeDbContextFactory { public ApplicationDbContext CreateDbContext(string[] args) { @@ -14,11 +14,14 @@ public sealed class ApplicationDbContextFactory : IDesignTimeDbContextFactory(); - var connectionString = configuration.GetConnectionString(DatabaseConfigName) + var connectionString = configuration.GetConnectionString(DatabaseConfigName) ?? throw new InvalidOperationException($"Connection string '{DatabaseConfigName}' was not found in configuration."); - - optionsBuilder.UseNpgsql(connectionString, npgsqlOptions => npgsqlOptions.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)); - return new ApplicationDbContext(optionsBuilder.Options); + optionsBuilder.UseNpgsql(connectionString, npgsqlOptions => + npgsqlOptions.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)); + + var designTimeTenantService = new DesignTimeTenantContextService(); + + return new ApplicationDbContext(optionsBuilder.Options, designTimeTenantService); } } \ No newline at end of file diff --git a/PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.Designer.cs b/PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.Designer.cs new file mode 100644 index 0000000..2fac050 --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.Designer.cs @@ -0,0 +1,2233 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PostFundManagement.Infrastructure.Database; + +#nullable disable + +namespace PostFundManagement.Infrastructure.Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260921085528_AddedTenant")] + partial class AddedTenant + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("PerformedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Activities", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.PrimitiveCollection("Changes") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .HasColumnType("bigint"); + + b.Property("EntityName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PerformedBy") + .HasColumnType("uuid"); + + b.Property("Timestamp") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("PerformedBy"); + + b.ToTable("AuditLogs", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Awards", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Beneficiary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Location") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("ReachedCount") + .HasColumnType("integer"); + + b.Property("TargetCount") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("Beneficiaries", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Budget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Budgets", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ChangeRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Justification") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("RevisedBudget") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("RevisedEndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("ChangeRequests", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ComplianceItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Requirements") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VerifiedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("VerifiedBy"); + + b.ToTable("ComplianceItems", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("EffectiveAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalSignatureId") + .HasColumnType("text"); + + b.Property("SignedDocumentUrl") + .HasColumnType("text"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TotalValue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("AwardId") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.ToTable("Contracts", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Disbursement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Disbursements", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Evidence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActivityId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DocumentUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("IndicatorId") + .HasColumnType("bigint"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("9"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.Property("Version") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.HasKey("Id"); + + b.HasIndex("ActivityId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("IndicatorId"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Evidences", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Indicator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("BaselineAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("TargetAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("UnitOfMeasure") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Indicators", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Instrument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.ToTable("Instruments", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Invite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("bigint"); + + b.Property("ContractId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("InvitedOrganisationId") + .HasColumnType("bigint"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Recipient") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AwardId"); + + b.HasIndex("ContractId"); + + b.HasIndex("InvitedOrganisationId"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("Recipient"); + + b.ToTable("Invites", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Issue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssignedTo") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Resolution") + .IsRequired() + .HasColumnType("text"); + + b.Property("ResolvedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("AssignedTo"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Issues", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Milestone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DueAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Milestones", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Error") + .HasColumnType("text"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Platform") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Recipient") + .HasColumnType("uuid"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Subject") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("Recipient"); + + b.HasIndex("TenantId"); + + b.ToTable("Notifications", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RegistrationNo") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("5"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Organisations", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Outcome", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BeneficiaryId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("BeneficiaryId"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Outcomes", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("OwnedBy") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("OwnedBy"); + + b.HasIndex("TenantId"); + + b.ToTable("Portfolios", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OwnedBy") + .HasColumnType("uuid"); + + b.Property("PortfolioId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OwnedBy"); + + b.HasIndex("PortfolioId"); + + b.HasIndex("TenantId"); + + b.ToTable("Programmes", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("ProjectType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("10"); + + b.Property("Sector") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("11"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Risk", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Impact") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Likelihood") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("3"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Risks", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Rule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.Property("Version") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Rules", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.SiteVisit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Findings") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)"); + + b.Property("InspectorName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("VisitedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("SiteVisits", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Tenants", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("TenantId"); + + b.ToTable("TenantOrganisations", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.ToTable("TenantUsers", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastLoginAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.AuditLog", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Performer") + .WithMany() + .HasForeignKey("PerformedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Performer"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Awards") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany("Awards") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Programme"); + + b.Navigation("Project"); + + b.Navigation("Tenant"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Beneficiary", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Budget", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ChangeRequest", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ComplianceItem", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Verifier") + .WithMany() + .HasForeignKey("VerifiedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("Verifier"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Contract", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Award", "Award") + .WithOne("Contract") + .HasForeignKey("PostFundManagement.Domain.Entities.Contract", "AwardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Award"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Disbursement", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Evidence", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Activity", "Activity") + .WithMany("Evidences") + .HasForeignKey("ActivityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Indicator", "Indicator") + .WithMany() + .HasForeignKey("IndicatorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Activity"); + + b.Navigation("Creator"); + + b.Navigation("Indicator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Indicator", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Instrument", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Instruments") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Invite", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Award", "Award") + .WithMany() + .HasForeignKey("AwardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Contract", "Contract") + .WithMany() + .HasForeignKey("ContractId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "InvitedOrganisation") + .WithMany() + .HasForeignKey("InvitedOrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "RecipientUser") + .WithMany() + .HasForeignKey("Recipient") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Award"); + + b.Navigation("Contract"); + + b.Navigation("InvitedOrganisation"); + + b.Navigation("Organisation"); + + b.Navigation("RecipientUser"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Issue", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Assignee") + .WithMany() + .HasForeignKey("AssignedTo") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Assignee"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Milestone", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Notification", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "RecipientUser") + .WithMany() + .HasForeignKey("Recipient") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organisation"); + + b.Navigation("RecipientUser"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Outcome", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Beneficiary", "Beneficiary") + .WithMany() + .HasForeignKey("BeneficiaryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Beneficiary"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Owner"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Portfolio", "Portfolio") + .WithMany("Programmes") + .HasForeignKey("PortfolioId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Owner"); + + b.Navigation("Portfolio"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Projects") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + + b.Navigation("Tenant"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Risk", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Rule", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Rules") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.SiteVisit", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Creator"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Tenant"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.Navigation("Evidences"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.Navigation("Contract"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.Navigation("Programmes"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.Navigation("Awards"); + + b.Navigation("Instruments"); + + b.Navigation("Projects"); + + b.Navigation("Rules"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.Navigation("Awards"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.cs b/PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.cs new file mode 100644 index 0000000..1f7a79e --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Migrations/20260921085528_AddedTenant.cs @@ -0,0 +1,370 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PostFundManagement.Infrastructure.Database.Migrations +{ + /// + public partial class AddedTenant : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TenantId", + table: "Projects", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "TenantId", + table: "Programmes", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "TenantId", + table: "Portfolios", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "TenantId", + table: "Notifications", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "TenantId", + table: "Awards", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.CreateTable( + name: "Tenants", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + CreatedBy = table.Column(type: "uuid", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + UpdatedBy = table.Column(type: "uuid", nullable: true), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Status = table.Column(type: "integer", nullable: false, defaultValueSql: "1") + }, + constraints: table => + { + table.PrimaryKey("PK_Tenants", x => x.Id); + table.ForeignKey( + name: "FK_Tenants_Users_CreatedBy", + column: x => x.CreatedBy, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Tenants_Users_UpdatedBy", + column: x => x.UpdatedBy, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "TenantOrganisations", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + CreatedBy = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "bigint", nullable: false), + OrganisationId = table.Column(type: "bigint", nullable: false), + Status = table.Column(type: "integer", nullable: false, defaultValueSql: "1") + }, + constraints: table => + { + table.PrimaryKey("PK_TenantOrganisations", x => x.Id); + table.ForeignKey( + name: "FK_TenantOrganisations_Organisations_OrganisationId", + column: x => x.OrganisationId, + principalTable: "Organisations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_TenantOrganisations_Tenants_TenantId", + column: x => x.TenantId, + principalTable: "Tenants", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_TenantOrganisations_Users_CreatedBy", + column: x => x.CreatedBy, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "TenantUsers", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + CreatedBy = table.Column(type: "uuid", nullable: false), + TenantId = table.Column(type: "bigint", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false, defaultValueSql: "1") + }, + constraints: table => + { + table.PrimaryKey("PK_TenantUsers", x => x.Id); + table.ForeignKey( + name: "FK_TenantUsers_Tenants_TenantId", + column: x => x.TenantId, + principalTable: "Tenants", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_TenantUsers_Users_CreatedBy", + column: x => x.CreatedBy, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_TenantUsers_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Projects_TenantId", + table: "Projects", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_Programmes_TenantId", + table: "Programmes", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_Portfolios_TenantId", + table: "Portfolios", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_Notifications_TenantId", + table: "Notifications", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_Awards_TenantId", + table: "Awards", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_TenantOrganisations_CreatedBy", + table: "TenantOrganisations", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_TenantOrganisations_OrganisationId", + table: "TenantOrganisations", + column: "OrganisationId"); + + migrationBuilder.CreateIndex( + name: "IX_TenantOrganisations_TenantId", + table: "TenantOrganisations", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_Tenants_CreatedBy", + table: "Tenants", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_Tenants_UpdatedBy", + table: "Tenants", + column: "UpdatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_TenantUsers_CreatedBy", + table: "TenantUsers", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_TenantUsers_TenantId", + table: "TenantUsers", + column: "TenantId"); + + migrationBuilder.CreateIndex( + name: "IX_TenantUsers_UserId", + table: "TenantUsers", + column: "UserId"); + + migrationBuilder.Sql(@" + DO $$ + BEGIN + IF EXISTS (SELECT 1 FROM ""Users"" WHERE ""Id"" = '18caeb45-65f6-4103-9bda-ddf073a24de2'::uuid) THEN + INSERT INTO ""Tenants"" (""Id"", ""Name"", ""Status"", ""CreatedBy"", ""CreatedAt"") + OVERRIDING SYSTEM VALUE + VALUES (1, 'Default Tenant', 1, '18caeb45-65f6-4103-9bda-ddf073a24de2'::uuid, NOW()) + ON CONFLICT (""Id"") DO NOTHING; + ELSE + RAISE EXCEPTION 'Target user 18caeb45-65f6-4103-9bda-ddf073a24de2 does not exist in Users table.'; + END IF; + END $$; + "); + + migrationBuilder.Sql(@"SELECT setval(pg_get_serial_sequence('""Tenants""', 'Id'), GREATEST(coalesce(max(""Id""), 1), 1)) FROM ""Tenants"";"); + + migrationBuilder.Sql("UPDATE \"Notifications\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;"); + migrationBuilder.Sql("UPDATE \"Awards\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;"); + migrationBuilder.Sql("UPDATE \"Portfolios\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;"); + migrationBuilder.Sql("UPDATE \"Programmes\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;"); + migrationBuilder.Sql("UPDATE \"Projects\" SET \"TenantId\" = 1 WHERE \"TenantId\" = 0;"); + + migrationBuilder.CreateIndex( + name: "IX_TenantOrganisations_TenantId_OrganisationId", + table: "TenantOrganisations", + columns: ["TenantId", "OrganisationId"], + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_TenantUsers_TenantId_UserId", + table: "TenantUsers", + columns: ["TenantId", "UserId"], + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Awards_Tenants_TenantId", + table: "Awards", + column: "TenantId", + principalTable: "Tenants", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Notifications_Tenants_TenantId", + table: "Notifications", + column: "TenantId", + principalTable: "Tenants", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Portfolios_Tenants_TenantId", + table: "Portfolios", + column: "TenantId", + principalTable: "Tenants", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Programmes_Tenants_TenantId", + table: "Programmes", + column: "TenantId", + principalTable: "Tenants", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Projects_Tenants_TenantId", + table: "Projects", + column: "TenantId", + principalTable: "Tenants", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Awards_Tenants_TenantId", + table: "Awards"); + + migrationBuilder.DropForeignKey( + name: "FK_Notifications_Tenants_TenantId", + table: "Notifications"); + + migrationBuilder.DropForeignKey( + name: "FK_Portfolios_Tenants_TenantId", + table: "Portfolios"); + + migrationBuilder.DropForeignKey( + name: "FK_Programmes_Tenants_TenantId", + table: "Programmes"); + + migrationBuilder.DropForeignKey( + name: "FK_Projects_Tenants_TenantId", + table: "Projects"); + + migrationBuilder.DropTable( + name: "TenantOrganisations"); + + migrationBuilder.DropTable( + name: "TenantUsers"); + + migrationBuilder.DropTable( + name: "Tenants"); + + migrationBuilder.DropIndex( + name: "IX_TenantOrganisations_TenantId_OrganisationId", + table: "TenantOrganisations"); + + migrationBuilder.DropIndex( + name: "IX_TenantUsers_TenantId_UserId", + table: "TenantUsers"); + + migrationBuilder.DropIndex( + name: "IX_Projects_TenantId", + table: "Projects"); + + migrationBuilder.DropIndex( + name: "IX_Programmes_TenantId", + table: "Programmes"); + + migrationBuilder.DropIndex( + name: "IX_Portfolios_TenantId", + table: "Portfolios"); + + migrationBuilder.DropIndex( + name: "IX_Notifications_TenantId", + table: "Notifications"); + + migrationBuilder.DropIndex( + name: "IX_Awards_TenantId", + table: "Awards"); + + migrationBuilder.DropColumn( + name: "TenantId", + table: "Projects"); + + migrationBuilder.DropColumn( + name: "TenantId", + table: "Programmes"); + + migrationBuilder.DropColumn( + name: "TenantId", + table: "Portfolios"); + + migrationBuilder.DropColumn( + name: "TenantId", + table: "Notifications"); + + migrationBuilder.DropColumn( + name: "TenantId", + table: "Awards"); + } + } +} diff --git a/PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.Designer.cs b/PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.Designer.cs new file mode 100644 index 0000000..0c14db6 --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.Designer.cs @@ -0,0 +1,2246 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PostFundManagement.Infrastructure.Database; + +#nullable disable + +namespace PostFundManagement.Infrastructure.Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260921125152_UpdateAuditLog")] + partial class UpdateAuditLog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("PerformedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Activities", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.PrimitiveCollection("Changes") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .IsRequired() + .HasColumnType("text"); + + b.Property("EntityName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PerformedBy") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.HasIndex("PerformedBy"); + + b.HasIndex("TenantId"); + + b.ToTable("AuditLogs", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Awards", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Beneficiary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Location") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("ReachedCount") + .HasColumnType("integer"); + + b.Property("TargetCount") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("Beneficiaries", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Budget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Budgets", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ChangeRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Justification") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("RevisedBudget") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("RevisedEndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("ChangeRequests", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ComplianceItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Requirements") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VerifiedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("VerifiedBy"); + + b.ToTable("ComplianceItems", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("EffectiveAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalSignatureId") + .HasColumnType("text"); + + b.Property("SignedDocumentUrl") + .HasColumnType("text"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TotalValue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("AwardId") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.ToTable("Contracts", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Disbursement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Disbursements", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Evidence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActivityId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DocumentUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("IndicatorId") + .HasColumnType("bigint"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("9"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.Property("Version") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.HasKey("Id"); + + b.HasIndex("ActivityId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("IndicatorId"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Evidences", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Indicator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("BaselineAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("TargetAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("UnitOfMeasure") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Indicators", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Instrument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.ToTable("Instruments", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Invite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("bigint"); + + b.Property("ContractId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("InvitedOrganisationId") + .HasColumnType("bigint"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Recipient") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AwardId"); + + b.HasIndex("ContractId"); + + b.HasIndex("InvitedOrganisationId"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("Recipient"); + + b.ToTable("Invites", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Issue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssignedTo") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Resolution") + .IsRequired() + .HasColumnType("text"); + + b.Property("ResolvedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("AssignedTo"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Issues", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Milestone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DueAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Milestones", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Error") + .HasColumnType("text"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Platform") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Recipient") + .HasColumnType("uuid"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Subject") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("Recipient"); + + b.HasIndex("TenantId"); + + b.ToTable("Notifications", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RegistrationNo") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("5"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Organisations", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Outcome", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BeneficiaryId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("BeneficiaryId"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Outcomes", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("OwnedBy") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("OwnedBy"); + + b.HasIndex("TenantId"); + + b.ToTable("Portfolios", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OwnedBy") + .HasColumnType("uuid"); + + b.Property("PortfolioId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OwnedBy"); + + b.HasIndex("PortfolioId"); + + b.HasIndex("TenantId"); + + b.ToTable("Programmes", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("ProjectType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("10"); + + b.Property("Sector") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("11"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Risk", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Impact") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Likelihood") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("3"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Risks", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Rule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.Property("Version") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Rules", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.SiteVisit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Findings") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)"); + + b.Property("InspectorName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("VisitedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("SiteVisits", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Tenants", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("TenantId"); + + b.ToTable("TenantOrganisations", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.ToTable("TenantUsers", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastLoginAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.AuditLog", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Performer") + .WithMany() + .HasForeignKey("PerformedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Performer"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Awards") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany("Awards") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Programme"); + + b.Navigation("Project"); + + b.Navigation("Tenant"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Beneficiary", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Budget", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ChangeRequest", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ComplianceItem", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Verifier") + .WithMany() + .HasForeignKey("VerifiedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("Verifier"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Contract", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Award", "Award") + .WithOne("Contract") + .HasForeignKey("PostFundManagement.Domain.Entities.Contract", "AwardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Award"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Disbursement", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Evidence", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Activity", "Activity") + .WithMany("Evidences") + .HasForeignKey("ActivityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Indicator", "Indicator") + .WithMany() + .HasForeignKey("IndicatorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Activity"); + + b.Navigation("Creator"); + + b.Navigation("Indicator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Indicator", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Instrument", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Instruments") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Invite", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Award", "Award") + .WithMany() + .HasForeignKey("AwardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Contract", "Contract") + .WithMany() + .HasForeignKey("ContractId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "InvitedOrganisation") + .WithMany() + .HasForeignKey("InvitedOrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "RecipientUser") + .WithMany() + .HasForeignKey("Recipient") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Award"); + + b.Navigation("Contract"); + + b.Navigation("InvitedOrganisation"); + + b.Navigation("Organisation"); + + b.Navigation("RecipientUser"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Issue", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Assignee") + .WithMany() + .HasForeignKey("AssignedTo") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Assignee"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Milestone", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Notification", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "RecipientUser") + .WithMany() + .HasForeignKey("Recipient") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organisation"); + + b.Navigation("RecipientUser"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Outcome", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Beneficiary", "Beneficiary") + .WithMany() + .HasForeignKey("BeneficiaryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Beneficiary"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Owner"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Portfolio", "Portfolio") + .WithMany("Programmes") + .HasForeignKey("PortfolioId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Owner"); + + b.Navigation("Portfolio"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Projects") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + + b.Navigation("Tenant"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Risk", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Rule", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Rules") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.SiteVisit", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Creator"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Tenant"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.Navigation("Evidences"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.Navigation("Contract"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.Navigation("Programmes"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.Navigation("Awards"); + + b.Navigation("Instruments"); + + b.Navigation("Projects"); + + b.Navigation("Rules"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.Navigation("Awards"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.cs b/PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.cs new file mode 100644 index 0000000..8deba19 --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Migrations/20260921125152_UpdateAuditLog.cs @@ -0,0 +1,64 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PostFundManagement.Infrastructure.Database.Migrations +{ + /// + public partial class UpdateAuditLog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "EntityId", + table: "AuditLogs", + type: "text", + nullable: false, + oldClrType: typeof(long), + oldType: "bigint"); + + migrationBuilder.AddColumn( + name: "TenantId", + table: "AuditLogs", + type: "bigint", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_AuditLogs_TenantId", + table: "AuditLogs", + column: "TenantId"); + + migrationBuilder.AddForeignKey( + name: "FK_AuditLogs_Tenants_TenantId", + table: "AuditLogs", + column: "TenantId", + principalTable: "Tenants", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AuditLogs_Tenants_TenantId", + table: "AuditLogs"); + + migrationBuilder.DropIndex( + name: "IX_AuditLogs_TenantId", + table: "AuditLogs"); + + migrationBuilder.DropColumn( + name: "TenantId", + table: "AuditLogs"); + + migrationBuilder.AlterColumn( + name: "EntityId", + table: "AuditLogs", + type: "bigint", + nullable: false, + oldClrType: typeof(string), + oldType: "text"); + } + } +} diff --git a/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs b/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs index 02f2c1f..36cd197 100644 --- a/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace PostFundManagement.Infrastructure.Database.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.11") + .HasAnnotation("ProductVersion", "10.0.12") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -111,8 +111,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.PrimitiveCollection("Changes") .HasColumnType("jsonb"); - b.Property("EntityId") - .HasColumnType("bigint"); + b.Property("EntityId") + .IsRequired() + .HasColumnType("text"); b.Property("EntityName") .IsRequired() @@ -122,6 +123,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.Property("PerformedBy") .HasColumnType("uuid"); + b.Property("TenantId") + .HasColumnType("bigint"); + b.Property("Timestamp") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") @@ -131,6 +135,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.HasIndex("PerformedBy"); + b.HasIndex("TenantId"); + b.ToTable("AuditLogs", (string)null); }); @@ -171,6 +177,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .HasColumnType("integer") .HasDefaultValueSql("1"); + b.Property("TenantId") + .HasColumnType("bigint"); + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); @@ -189,6 +198,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.HasIndex("ProjectId"); + b.HasIndex("TenantId"); + b.HasIndex("UpdatedBy"); b.ToTable("Awards", (string)null); @@ -883,6 +894,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .HasMaxLength(256) .HasColumnType("character varying(256)"); + b.Property("TenantId") + .HasColumnType("bigint"); + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); @@ -892,6 +906,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.HasIndex("Recipient"); + b.HasIndex("TenantId"); + b.ToTable("Notifications", (string)null); }); @@ -1012,6 +1028,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.Property("OwnedBy") .HasColumnType("uuid"); + b.Property("TenantId") + .HasColumnType("bigint"); + b.HasKey("Id"); b.HasIndex("CreatedBy"); @@ -1020,6 +1039,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.HasIndex("OwnedBy"); + b.HasIndex("TenantId"); + b.ToTable("Portfolios", (string)null); }); @@ -1055,6 +1076,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .HasColumnType("integer") .HasDefaultValueSql("1"); + b.Property("TenantId") + .HasColumnType("bigint"); + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); @@ -1069,6 +1093,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.HasIndex("PortfolioId"); + b.HasIndex("TenantId"); + b.ToTable("Programmes", (string)null); }); @@ -1122,6 +1148,9 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .HasColumnType("integer") .HasDefaultValueSql("1"); + b.Property("TenantId") + .HasColumnType("bigint"); + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); @@ -1134,6 +1163,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.HasIndex("ProgrammeId"); + b.HasIndex("TenantId"); + b.HasIndex("UpdatedBy"); b.ToTable("Projects", (string)null); @@ -1282,6 +1313,123 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.ToTable("SiteVisits", (string)null); }); + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Tenants", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("TenantId"); + + b.ToTable("TenantOrganisations", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.ToTable("TenantUsers", (string)null); + }); + modelBuilder.Entity("PostFundManagement.Domain.Entities.User", b => { b.Property("Id") @@ -1355,7 +1503,14 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.NoAction); + b.Navigation("Performer"); + + b.Navigation("Tenant"); }); modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => @@ -1388,6 +1543,12 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .HasForeignKey("ProjectId") .OnDelete(DeleteBehavior.SetNull); + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") .WithMany() .HasForeignKey("UpdatedBy") @@ -1403,6 +1564,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.Navigation("Project"); + b.Navigation("Tenant"); + b.Navigation("Updator"); }); @@ -1761,9 +1924,17 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.Navigation("Organisation"); b.Navigation("RecipientUser"); + + b.Navigation("Tenant"); }); modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b => @@ -1816,11 +1987,19 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .OnDelete(DeleteBehavior.NoAction) .IsRequired(); + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.Navigation("Creator"); b.Navigation("Organisation"); b.Navigation("Owner"); + + b.Navigation("Tenant"); }); modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => @@ -1843,11 +2022,19 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .OnDelete(DeleteBehavior.NoAction) .IsRequired(); + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + b.Navigation("Creator"); b.Navigation("Owner"); b.Navigation("Portfolio"); + + b.Navigation("Tenant"); }); modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => @@ -1864,6 +2051,12 @@ namespace PostFundManagement.Infrastructure.Database.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") .WithMany() .HasForeignKey("UpdatedBy") @@ -1873,6 +2066,8 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.Navigation("Programme"); + b.Navigation("Tenant"); + b.Navigation("Updator"); }); @@ -1940,6 +2135,78 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.Navigation("Project"); }); + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Creator"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Tenant"); + + b.Navigation("User"); + }); + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => { b.Navigation("Evidences"); diff --git a/PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs b/PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs new file mode 100644 index 0000000..66c1707 --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs @@ -0,0 +1,14 @@ +using PostFundManagement.Domain.Abstractions; + +namespace PostFundManagement.Infrastructure.Database.Factories; + +public sealed partial class ApplicationDbContextFactory +{ + private sealed class DesignTimeTenantContextService : ITenantContextService + { + public string? TenantName => "Design Time"; + public long TenantId => 0L; + public bool IsAdmin => true; + public bool HasTenantFilter => false; + } +} \ No newline at end of file diff --git a/PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs b/PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs new file mode 100644 index 0000000..9eae36f --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs @@ -0,0 +1,29 @@ +using PostFundManagement.Domain.Abstractions; + +namespace PostFundManagement.Infrastructure.Database.Services; + +public sealed class TenantContextService : ITenantContextService +{ + public string? TenantName { get; } + + public long TenantId { get; } + + public bool IsAdmin { get; } + + public bool HasTenantFilter { get; } + + public TenantContextService(IHttpContextAccessor httpContextAccessor) + { + var user = httpContextAccessor.HttpContext?.User; + + IsAdmin = user?.IsInRole("Admin") == true || user?.HasClaim("role", "Admin") == true; + + HasTenantFilter = !IsAdmin; + + var tenantIdClaimVal = user?.FindFirst("tenant_id")?.Value ?? user?.FindFirst("TenantId")?.Value; + TenantId = long.TryParse(tenantIdClaimVal, out var id) ? id : 0L; + + var tenantNameClaimVal = user?.FindFirst("tenant")?.Value ?? user?.FindFirst("Tenant")?.Value; + TenantName = !string.IsNullOrWhiteSpace(tenantNameClaimVal) ? tenantNameClaimVal : null; + } +} \ No newline at end of file diff --git a/PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj b/PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj index 485285a..de366dd 100644 --- a/PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj +++ b/PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj @@ -36,6 +36,7 @@ + From 11311421ad65e157700c4ee70ce7a6a65df937e4 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 22 Sep 2026 05:44:25 +0200 Subject: [PATCH 3/4] Removed ValueTask generally where not needed Removed relationships from audit log --- .../Abstractions/S3ServiceBase.cs | 12 +- .../Services/ICommunicationsService.cs | 12 +- .../Abstractions/Services/IEvidenceService.cs | 34 +- .../Services/IExecutionService.cs | 24 +- .../Services/IGovernanceService.cs | 29 +- .../Abstractions/Services/IGrantsService.cs | 33 +- .../Abstractions/Services/IIdentityService.cs | 2 +- .../Abstractions/Services/IProjectService.cs | 46 +- .../Abstractions/Services/IToolService.cs | 2 +- .../Abstractions/Services/IUserService.cs | 11 +- .../Interceptors/AuditInterceptor.cs | 2 +- .../CommunicationsCacheService.cs | 16 +- .../Communications/CommunicationsService.cs | 18 +- .../Services/Evidence/EvidenceCacheService.cs | 44 +- .../Services/Evidence/EvidenceService.cs | 48 +- .../Execution/ExecutionCacheService.cs | 32 +- .../Services/Execution/ExecutionService.cs | 38 +- .../Governance/GovernanceCacheService.cs | 45 +- .../Services/Governance/GovernanceService.cs | 50 +- .../Services/Grants/GrantsCacheService.cs | 47 +- .../Services/Grants/GrantsService.cs | 52 +- .../Services/Projects/ProjectCacheService.cs | 70 +- .../Services/Projects/ProjectService.cs | 76 +- .../Services/Shared/AuditService.cs | 4 +- .../Shared/BrowserLocalStorageService.cs | 20 +- .../Services/Shared/EmailService.cs | 6 +- .../Services/Shared/HashService.cs | 2 +- .../Services/Shared/IdentityCacheService.cs | 2 +- .../Services/Shared/IdentityService.cs | 4 +- .../Services/Shared/TokenService.cs | 6 +- .../Services/Tools/ToolCacheService.cs | 4 +- .../Services/Tools/ToolService.cs | 4 +- .../Services/Users/UserCacheService.cs | 15 +- .../Services/Users/UserService.cs | 16 +- .../Abstractions/IS3Service.cs | 6 +- .../Entities/AuditLog.cs | 19 +- ...movedRelationshipsFromAuditLog.Designer.cs | 2224 +++++++++++++++++ ...033954_RemovedRelationshipsFromAuditLog.cs | 59 + .../ApplicationDbContextModelSnapshot.cs | 22 - 39 files changed, 2700 insertions(+), 456 deletions(-) create mode 100644 PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.Designer.cs create mode 100644 PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.cs diff --git a/PostFundManagement.Application/Abstractions/S3ServiceBase.cs b/PostFundManagement.Application/Abstractions/S3ServiceBase.cs index 6063984..d700573 100644 --- a/PostFundManagement.Application/Abstractions/S3ServiceBase.cs +++ b/PostFundManagement.Application/Abstractions/S3ServiceBase.cs @@ -12,8 +12,8 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3) public string GenerateCdnUrl(string fileKey) => $"{CdnBaseUrl}/{fileKey}"; - public virtual async ValueTask> GeneratePresignedUrlAsync(string fileKey, TimeSpan duration, string? contentType = null, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public virtual async Task> GeneratePresignedUrlAsync(string fileKey, TimeSpan duration, string? contentType = null, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (string.IsNullOrWhiteSpace(BucketName)) return Result.Fail(ValidationError.Create("Bucket name is not configured.")); @@ -51,8 +51,8 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3) : $"{cleanSubPath}/{fileHash.ToLower(CultureInfo.InvariantCulture)}_{fileName}"; } - public virtual async ValueTask> UploadFileAsync(string fileName, string subPath, Stream fileStream, string contentType, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public virtual async Task> UploadFileAsync(string fileName, string subPath, Stream fileStream, string contentType, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (string.IsNullOrWhiteSpace(BucketName)) return Result.Fail(ValidationError.Create("Bucket name is not configured.")); @@ -92,8 +92,8 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3) : Result.Ok(fileKey); })); - public virtual async ValueTask DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public virtual async Task DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { if (string.IsNullOrWhiteSpace(BucketName)) return Result.Fail(NotFoundError.Create("Bucket name is not configured.")); diff --git a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs index 10aebbb..238f392 100644 --- a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs +++ b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs @@ -1,18 +1,16 @@ using PostFundManagement.Application.Services.Communications; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface ICommunicationsService -{ - [Audit] - ValueTask> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default); +{ + Task> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default); + Task> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default); - ValueTask> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default); + Task> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs b/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs index 751e11b..59ce49b 100644 --- a/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs @@ -1,38 +1,32 @@ using PostFundManagement.Application.Services.Evidence; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IEvidenceService -{ - [Audit] - ValueTask CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default); +{ + Task CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default); + + Task RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default); + Task> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default); + Task> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken); - ValueTask> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken); + Task> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default); - ValueTask> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default); + Task> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default); - ValueTask> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default); + Task> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default); - ValueTask> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default); + Task UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default); - - ValueTask> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default); + Task> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs b/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs index 6cd37bc..d611c66 100644 --- a/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs @@ -1,32 +1,26 @@ using PostFundManagement.Application.Services.Execution; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IExecutionService { - ValueTask> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default); + Task ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default); + Task> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default); - ValueTask> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default); + Task> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default); + Task UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); + Task UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs index 477125e..08039ec 100644 --- a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs @@ -1,37 +1,32 @@ using PostFundManagement.Application.Services.Governance; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IGovernanceService { - ValueTask> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default); + Task> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default); - ValueTask> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default); + Task> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default); - ValueTask> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default); + Task> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default); - ValueTask> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default); + Task> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default); - ValueTask> GetDashboardAsync(CancellationToken cancellationToken = default); + Task> GetDashboardAsync(CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default); + Task> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); + Task UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default); + Task> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default); - ValueTask> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default); + Task> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs b/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs index 22dce48..b7e0556 100644 --- a/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs @@ -1,41 +1,34 @@ using PostFundManagement.Application.Services.Grants; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IGrantsService { - ValueTask> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default); + Task> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default); - ValueTask> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default); + Task> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default); - ValueTask> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default); + Task> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default); - [Audit] - ValueTask ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default); + Task ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default); + Task> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default); - [Audit] - ValueTask UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default); + Task UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default); + Task UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs b/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs index 9056366..d4efd5a 100644 --- a/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IIdentityService.cs @@ -5,5 +5,5 @@ namespace PostFundManagement.Application.Abstractions.Services; public interface IIdentityService { [Audit] - ValueTask UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default); + Task UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs index b5f80d5..4656f3d 100644 --- a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs @@ -1,54 +1,46 @@ using PostFundManagement.Application.Services.Projects; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IProjectService { - [Audit] - ValueTask UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default); + Task UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default); + Task> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default); - ValueTask> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default); + Task> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default); + Task> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default); - ValueTask> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default); + Task> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default); - ValueTask> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default); + Task> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default); - ValueTask> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); + Task UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); + Task UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default); + Task> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default); - ValueTask> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default); + Task> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default); - [Audit] - ValueTask CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); + Task CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); + Task UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); - ValueTask> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default); + Task> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default); - ValueTask> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default); + Task> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default); - ValueTask> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default); + Task> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IToolService.cs b/PostFundManagement.Application/Abstractions/Services/IToolService.cs index 6fdfb0d..c848b6f 100644 --- a/PostFundManagement.Application/Abstractions/Services/IToolService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IToolService.cs @@ -2,5 +2,5 @@ public interface IToolService { - ValueTask> EncodeLong(long id, CancellationToken cancellationToken); + Task> EncodeLong(long id, CancellationToken cancellationToken); } diff --git a/PostFundManagement.Application/Abstractions/Services/IUserService.cs b/PostFundManagement.Application/Abstractions/Services/IUserService.cs index 9b6d5c3..e9f2da8 100644 --- a/PostFundManagement.Application/Abstractions/Services/IUserService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IUserService.cs @@ -1,19 +1,16 @@ using PostFundManagement.Application.Services.Users; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IUserService { - [Audit] - ValueTask SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default); + Task SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default); - ValueTask> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default); + Task> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default); - [Audit] - ValueTask UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default); + Task UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default); - ValueTask> GetUserAsync(Guid userId, CancellationToken cancellationToken); + Task> GetUserAsync(Guid userId, CancellationToken cancellationToken); } diff --git a/PostFundManagement.Application/Interceptors/AuditInterceptor.cs b/PostFundManagement.Application/Interceptors/AuditInterceptor.cs index 02f2aab..5b30abc 100644 --- a/PostFundManagement.Application/Interceptors/AuditInterceptor.cs +++ b/PostFundManagement.Application/Interceptors/AuditInterceptor.cs @@ -73,7 +73,7 @@ public sealed class AuditInterceptor(AuditService auditService, IHttpContextAcce { var genericDef = returnType.GetGenericTypeDefinition(); - if (genericDef == typeof(Task<>) || genericDef == typeof(ValueTask<>)) + if (genericDef == typeof(Task<>) || genericDef == typeof(Task<>)) innerType = returnType.GetGenericArguments()[0]; } diff --git a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs index 4d98bae..303ff65 100644 --- a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs +++ b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs @@ -3,6 +3,7 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -11,8 +12,8 @@ namespace PostFundManagement.Application.Services.Communications; public sealed class CommunicationsCacheService(CommunicationsService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, ICommunicationsService { - public async ValueTask> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetApplicantInviteAsync(applicantUserId, cancellationToken); @@ -33,8 +34,8 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID return Result.Ok(cached!.FromJson()); })); - public async ValueTask> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken); @@ -57,8 +58,8 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetInvitesAsync(pagination, cancellationToken); @@ -81,6 +82,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID return Result.Ok(cached.FromJson()); })); - public async ValueTask> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.SendInviteAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Communications/CommunicationsService.cs b/PostFundManagement.Application/Services/Communications/CommunicationsService.cs index 3cdf506..0de5db7 100644 --- a/PostFundManagement.Application/Services/Communications/CommunicationsService.cs +++ b/PostFundManagement.Application/Services/Communications/CommunicationsService.cs @@ -8,9 +8,9 @@ using PostFundManagement.Infrastructure.Database; namespace PostFundManagement.Application.Services.Communications; public sealed class CommunicationsService(IDbContextFactory contextFactory) : ICommunicationsService -{ - public async ValueTask> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => +{ + public async Task> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -42,8 +42,8 @@ public sealed class CommunicationsService(IDbContextFactory> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -56,8 +56,8 @@ public sealed class CommunicationsService(IDbContextFactory i.Map()).ToArray()); })); - public async ValueTask> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => + public async Task> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -67,8 +67,8 @@ public sealed class CommunicationsService(IDbContextFactory> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); diff --git a/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs b/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs index 57a39d9..8aea3fb 100644 --- a/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs +++ b/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs @@ -3,6 +3,7 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -11,17 +12,20 @@ namespace PostFundManagement.Application.Services.Evidence; public sealed class EvidenceCacheService(EvidenceService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IEvidenceService { - public async ValueTask CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default) => await service.CreateEvidenceAsync(userId, form, file, cancellationToken); - public async ValueTask CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateIndicatorAsync(request, userId, cancellationToken); - public async ValueTask CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateMilestoneAsync(request, userId, cancellationToken); - public async ValueTask> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetEvidenceAsync(evidenceId, cancellationToken); @@ -44,8 +48,8 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetEvidenceFilesAsync(pagination, cancellationToken); @@ -68,8 +72,8 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetEvidenceUrlAsync(evidenceIdHash, cancellationToken); @@ -92,8 +96,8 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached); })); - public async ValueTask> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetIndicatorAsync(indicatorId, cancellationToken); @@ -116,8 +120,8 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetIndicatorsAsync(pagination, cancellationToken); @@ -140,8 +144,8 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetMilestoneEvidencesAsync(milestoneIdHash, cancellationToken); @@ -164,8 +168,8 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProjectEvidencesAsync(projectIdHash, cancellationToken); @@ -188,9 +192,11 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached.FromJson()); })); - public async ValueTask RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) => await service.RecordIndicatorActualAmountAsync(indicatorIdHash, amount, userId, cancellationToken); - public async ValueTask UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateEvidenceAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Evidence/EvidenceService.cs b/PostFundManagement.Application/Services/Evidence/EvidenceService.cs index 851275b..e293722 100644 --- a/PostFundManagement.Application/Services/Evidence/EvidenceService.cs +++ b/PostFundManagement.Application/Services/Evidence/EvidenceService.cs @@ -12,8 +12,8 @@ namespace PostFundManagement.Application.Services.Evidence; public sealed class EvidenceService(IDbContextFactory contextFactory, HashService hashService, [FromKeyedServices(Constants.GeneralBucketName)] IS3Service s3Service) : IEvidenceService { - public async ValueTask CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { if (!long.TryParse(form["projectId"], out var projectId) || !long.TryParse(form["milestoneId"], out var milestoneId) || !long.TryParse(form["indicatorId"], out var indicatorId) || !long.TryParse(form["activityId"], out var activityId) || @@ -49,8 +49,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { var decodeResult = hashService.DecodeLongIdHash(indicatorIdHash); @@ -68,8 +68,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.OkIf(changedTotal > 0, UpdateError.New); })); - public async ValueTask> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -82,8 +82,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.Ok(indicators.Select(i => i.Map()).ToArray()); })); - public async ValueTask> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -96,8 +96,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.Ok(evidence.Select(i => i.Map()).ToArray()); })); - public async ValueTask CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -122,8 +122,8 @@ public sealed class EvidenceService(IDbContextFactory cont CreateError.Create("No record could be created.")); })); - public async ValueTask CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -145,8 +145,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => + public async Task> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(indicatorId); @@ -162,8 +162,8 @@ public sealed class EvidenceService(IDbContextFactory cont .ToResult(indicator!.Map()); })); - public async ValueTask> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(evidenceId); @@ -179,8 +179,8 @@ public sealed class EvidenceService(IDbContextFactory cont .ToResult(evidence!.Map()); })); - public async ValueTask> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(milestoneIdHash); @@ -199,8 +199,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.Ok(evidences.Select(e => e.Map()).ToArray()); })); - public async ValueTask> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(projectIdHash); @@ -219,8 +219,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.Ok(evidences.Select(e => e.Map()).ToArray()); })); - public async ValueTask UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -238,8 +238,8 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.OkIf(changedTotal > 0, UpdateError.New); })); - public async ValueTask> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(evidenceIdHash); diff --git a/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs b/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs index acf9f19..c15cb68 100644 --- a/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs +++ b/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs @@ -3,6 +3,7 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -11,17 +12,20 @@ namespace PostFundManagement.Application.Services.Execution; public sealed class ExecutionCacheService(ExecutionService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IExecutionService { - public async ValueTask ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) => await service.ApproveDisbursementAsync(disbursementIdHash, userId, cancellationToken); - public async ValueTask CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateDisbursementAsync(request, userId, cancellationToken); - public async ValueTask CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateRiskAsync(request, userId, cancellationToken); - public async ValueTask> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetDisbursementAsync(disbursementIdHash, cancellationToken); @@ -44,8 +48,8 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetDisbursementsAsync(pagination, cancellationToken); @@ -68,8 +72,8 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetRiskAsync(riskIdHash, cancellationToken); @@ -92,8 +96,8 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetRisksAsync(pagination, cancellationToken); @@ -116,9 +120,11 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed return Result.Ok(cached.FromJson()); })); - public async ValueTask UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateDisbursementAsync(request, userId, cancellationToken); - public async ValueTask UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) => await service.UpdateRiskAsync(request, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Execution/ExecutionService.cs b/PostFundManagement.Application/Services/Execution/ExecutionService.cs index 952a32d..b1fd278 100644 --- a/PostFundManagement.Application/Services/Execution/ExecutionService.cs +++ b/PostFundManagement.Application/Services/Execution/ExecutionService.cs @@ -10,8 +10,8 @@ namespace PostFundManagement.Application.Services.Execution; public sealed class ExecutionService(IDbContextFactory contextFactory, HashService hashService) : IExecutionService { - public async ValueTask> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -24,8 +24,8 @@ public sealed class ExecutionService(IDbContextFactory con return Result.Ok(risks.Select(i => i.Map()).ToArray()); })); - public async ValueTask> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -37,9 +37,9 @@ public sealed class ExecutionService(IDbContextFactory con return Result.Ok(disbursements.Select(i => i.Map()).ToArray()); })); - - public async ValueTask CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + + public async Task CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -60,8 +60,8 @@ public sealed class ExecutionService(IDbContextFactory con return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -81,8 +81,8 @@ public sealed class ExecutionService(IDbContextFactory con return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { var decodeResult = hashService.DecodeLongIdHash(disbursementIdHash); @@ -101,8 +101,8 @@ public sealed class ExecutionService(IDbContextFactory con return Result.OkIf(changedTotal > 0, UpdateError.New); })); - public async ValueTask> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => + public async Task> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(disbursementIdHash); @@ -119,8 +119,8 @@ public sealed class ExecutionService(IDbContextFactory con .ToResult(disbursement!.Map()); })); - public async ValueTask> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(riskIdHash); @@ -137,8 +137,8 @@ public sealed class ExecutionService(IDbContextFactory con .ToResult(risk!.Map()); })); - public async ValueTask UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -153,8 +153,8 @@ public sealed class ExecutionService(IDbContextFactory con return Result.OkIf(changedTotal > 0, UpdateError.New); })); - public async ValueTask UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); diff --git a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs index 283d047..8c6e154 100644 --- a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs +++ b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs @@ -3,6 +3,7 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -11,14 +12,16 @@ namespace PostFundManagement.Application.Services.Governance; public sealed class GovernanceCacheService(GovernanceService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IGovernanceService { - public async ValueTask CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateOrganisationAsync(request, userId, cancellationToken); - public async ValueTask CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreatePortfolioAsync(request, userId, cancellationToken); - public async ValueTask> GetDashboardAsync(CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetDashboardAsync(CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetDashboardAsync(cancellationToken); @@ -41,8 +44,8 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetInstrumentsAsync(programmeIdHash, cancellationToken); @@ -65,8 +68,8 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetOrganisationAsync(organisationIdHash, cancellationToken); @@ -89,8 +92,8 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetOrganisationsAsync(pagination, cancellationToken); @@ -113,8 +116,8 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetPorfoliosAsync(organisationIdHash, cancellationToken); @@ -137,8 +140,8 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetPortfolioAsync(portfolioIdHash, cancellationToken); @@ -161,8 +164,8 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetPortfolioProgrammesAsync(portfolioIdHash, cancellationToken); @@ -185,8 +188,8 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProgrammesAsync(portfolioIdHash, cancellationToken); @@ -209,9 +212,11 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - public async ValueTask> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) => + [Audit] + public async Task> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) => await service.UpdateOrganisationAsync(request, cancellationToken); - public async ValueTask UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdatePortfolioAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Governance/GovernanceService.cs b/PostFundManagement.Application/Services/Governance/GovernanceService.cs index 53e1e03..ae6fe56 100644 --- a/PostFundManagement.Application/Services/Governance/GovernanceService.cs +++ b/PostFundManagement.Application/Services/Governance/GovernanceService.cs @@ -1,18 +1,18 @@ using PostFundManagement.Application.Abstractions.Services; +using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; using PostFundManagement.Domain.Errors; using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Models; using PostFundManagement.Infrastructure.Database; -using PostFundManagement.Application.Extensions; namespace PostFundManagement.Application.Services.Governance; public sealed class GovernanceService(IDbContextFactory contextFactory, HashService hashService) : IGovernanceService { - public async ValueTask> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(portfolioIdHash); @@ -30,8 +30,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.Ok(programmes.Select(p => p.Map()).ToArray()); })); - public async ValueTask> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(organisationIdHash); @@ -49,8 +49,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.Ok(portfolios.Select(p => p.Map()).ToArray()); })); - public async ValueTask> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -63,8 +63,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.Ok(organisations.Select(o => o.Map()).ToArray()); })); - public async ValueTask> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(organisationIdHash); @@ -81,8 +81,8 @@ public sealed class GovernanceService(IDbContextFactory co .ToResult(organisation!.Map()); })); - public async ValueTask> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(programmeIdHash); @@ -100,8 +100,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.Ok(instruments.Select(i => i.Map()).ToArray()); })); - public async ValueTask> GetDashboardAsync(CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetDashboardAsync(CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -126,8 +126,8 @@ public sealed class GovernanceService(IDbContextFactory co disbursedAmount, onTrackPercentage, atRiskCount, beneficiariesReached)); })); - public async ValueTask CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -150,8 +150,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -166,8 +166,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.OkIf(changedTotal > 0, UpdateError.New); })); - public async ValueTask CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -195,8 +195,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -213,8 +213,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.OkIf(changedTotal > 0, UpdateError.Create("Nothing was saved, no change detected.")); })); - public async ValueTask> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(portfolioIdHash); @@ -232,8 +232,8 @@ public sealed class GovernanceService(IDbContextFactory co return Result.Ok(programmes.Select(p => p.Map()).ToArray()); })); - public async ValueTask> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(portfolioIdHash); diff --git a/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs b/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs index 2427f04..c002711 100644 --- a/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs +++ b/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs @@ -3,6 +3,7 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -11,20 +12,24 @@ namespace PostFundManagement.Application.Services.Grants; public sealed class GrantsCacheService(GrantsService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IGrantsService { - public async ValueTask CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateAwardAsync(request, userId, cancellationToken); - public async ValueTask CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateBudgetAsync(request, userId, cancellationToken); - public async ValueTask CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateContractAsync(request, userId, cancellationToken); - public async ValueTask ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.ExecuteContractAsync(request, userId, cancellationToken); - public async ValueTask> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetAwardAsync(awardIdHash, cancellationToken); @@ -47,8 +52,8 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetAwardsAsync(pagination, cancellationToken); @@ -71,8 +76,8 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetBudgetAsync(budgetIdHash, cancellationToken); @@ -95,8 +100,8 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetBudgetsAsync(pagination, cancellationToken); @@ -119,8 +124,8 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetContractAsync(contractIdHash, cancellationToken); @@ -143,8 +148,8 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetContractsAsync(pagination, cancellationToken); @@ -167,8 +172,8 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetContractUrlAsync(contractIdHash, cancellationToken); @@ -191,9 +196,11 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached!); })); - public async ValueTask UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default) => await service.UpdateContractAsync(request, cancellationToken); - public async ValueTask UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default) => + [Audit] + public async Task UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default) => await service.UploadContractAsync(contractIdHash, userId, file, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Grants/GrantsService.cs b/PostFundManagement.Application/Services/Grants/GrantsService.cs index ac31e32..3784947 100644 --- a/PostFundManagement.Application/Services/Grants/GrantsService.cs +++ b/PostFundManagement.Application/Services/Grants/GrantsService.cs @@ -12,8 +12,8 @@ namespace PostFundManagement.Application.Services.Grants; public sealed class GrantsService(IDbContextFactory contextFactory, HashService hashService, [FromKeyedServices(Constants.GeneralBucketName)] IS3Service s3Service) : IGrantsService { - public async ValueTask> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -26,8 +26,8 @@ public sealed class GrantsService(IDbContextFactory contex return Result.Ok(contracts.Select(c => c.Map()).ToArray()); })); - public async ValueTask> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(contractIdHash); @@ -44,8 +44,8 @@ public sealed class GrantsService(IDbContextFactory contex .ToResult(contract!.Map()); })); - public async ValueTask> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -58,8 +58,8 @@ public sealed class GrantsService(IDbContextFactory contex return Result.Ok(budgets.Select(b => b.Map()).ToArray()); })); - public async ValueTask> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(budgetIdHash); @@ -76,8 +76,8 @@ public sealed class GrantsService(IDbContextFactory contex .ToResult(budget!.Map()); })); - public async ValueTask> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -90,8 +90,8 @@ public sealed class GrantsService(IDbContextFactory contex return Result.Ok(awards.Select(a => a.Map()).ToArray()); })); - public async ValueTask> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(awardIdHash); @@ -107,8 +107,8 @@ public sealed class GrantsService(IDbContextFactory contex .ToResult(award!.Map()); })); - public async ValueTask ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -123,8 +123,8 @@ public sealed class GrantsService(IDbContextFactory contex return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, UpdateError.New); })); - public async ValueTask CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -150,8 +150,8 @@ public sealed class GrantsService(IDbContextFactory contex return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { if (request.Amount <= 0) return Result.Fail(ValidationError.Create("Budget amount must be greater than zero.")); @@ -175,8 +175,8 @@ public sealed class GrantsService(IDbContextFactory contex return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { if (request.Amount <= 0) return Result.Fail(ValidationError.Create("Approved amount must be greater than zero.")); @@ -204,8 +204,8 @@ public sealed class GrantsService(IDbContextFactory contex return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(contractIdHash); @@ -231,8 +231,8 @@ public sealed class GrantsService(IDbContextFactory contex .ToResult(secureDownloadUrlResult.Value); })); - public async ValueTask UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { var decodeResult = hashService.DecodeLongIdHash(contractIdHash); @@ -261,8 +261,8 @@ public sealed class GrantsService(IDbContextFactory contex CreateError.Create("Failed to upload contract file")); })); - public async ValueTask UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); diff --git a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs index 6dd75d2..e19e611 100644 --- a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs +++ b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs @@ -3,6 +3,7 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -11,17 +12,20 @@ namespace PostFundManagement.Application.Services.Projects; public sealed class ProjectCacheService(ProjectService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IProjectService { - public async ValueTask CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateActivityAsync(request, userId, cancellationToken); - public async ValueTask CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateProgrammeAsync(request, userId, cancellationToken); - public async ValueTask CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateProjectAsync(request, userId, cancellationToken); - public async ValueTask> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetActivitiesAsync(projectIdHash, cancellationToken); @@ -44,8 +48,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetActivityAsync(activityIdHash, cancellationToken); @@ -68,8 +72,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetMilestoneAsync(milestoneIdHash, cancellationToken); @@ -92,8 +96,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetMilestonesAsync(pagination, cancellationToken); @@ -116,8 +120,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProgrammeAsync(programmeIdHash, cancellationToken); @@ -140,8 +144,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached!.FromJson()); })); - public async ValueTask> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProgrammeProjectsAsync(programmeIdHash, cancellationToken); @@ -164,8 +168,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProgrammesAsync(pagination, cancellationToken); @@ -188,8 +192,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProjectAsync(projectIdHash, cancellationToken); @@ -212,8 +216,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProjectIndicatorsAsync(projectIdHash, cancellationToken); @@ -236,8 +240,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProjectMilestonesAsync(projectIdHash, cancellationToken); @@ -260,8 +264,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProjectRisksAsync(projectIdHash, cancellationToken); @@ -284,8 +288,8 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetProjectsAsync(pagination, cancellationToken); @@ -308,15 +312,19 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - public async ValueTask UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateMilestoneAsync(request, userId, cancellationToken); - public async ValueTask UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProgrammeAsync(request, userId, cancellationToken); - public async ValueTask UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProjectAsync(request, userId, cancellationToken); - public async ValueTask UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProjectStatusAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Projects/ProjectService.cs b/PostFundManagement.Application/Services/Projects/ProjectService.cs index a53a5e4..60d41b3 100644 --- a/PostFundManagement.Application/Services/Projects/ProjectService.cs +++ b/PostFundManagement.Application/Services/Projects/ProjectService.cs @@ -10,8 +10,8 @@ namespace PostFundManagement.Application.Services.Projects; public sealed class ProjectService(IDbContextFactory contextFactory, HashService hashService) : IProjectService { - public async ValueTask UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -28,8 +28,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled.")); })); - public async ValueTask> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -41,8 +41,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(projects.Select(p => p.Map()).ToArray()); })); - public async ValueTask> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(projectIdHash); @@ -59,8 +59,8 @@ public sealed class ProjectService(IDbContextFactory conte .ToResult(project!.Map()); })); - public async ValueTask> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(projectIdHash); @@ -77,8 +77,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(activities.Select(a => a.Map()).ToArray()); })); - public async ValueTask CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -108,8 +108,8 @@ public sealed class ProjectService(IDbContextFactory conte CreateError.Create("No changes detected, update cancelled.")); })); - public async ValueTask CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -138,8 +138,8 @@ public sealed class ProjectService(IDbContextFactory conte CreateError.Create("No record was created.")); })); - public async ValueTask> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(projectIdHash); @@ -157,8 +157,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(milestones.Select(i => i.Map()).ToArray()); })); - public async ValueTask> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(projectIdHash); @@ -176,8 +176,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(evidences.Select(e => e.Map()).ToArray()); })); - public async ValueTask> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(milestoneIdHash); @@ -193,8 +193,8 @@ public sealed class ProjectService(IDbContextFactory conte .ToResult(milestone!.Map()); })); - public async ValueTask> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -207,8 +207,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(milestones.Select(i => i.Map()).ToArray()); })); - public async ValueTask UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -227,8 +227,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled.")); })); - public async ValueTask UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -246,8 +246,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled.")); })); - public async ValueTask> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(activityIdHash); @@ -264,8 +264,8 @@ public sealed class ProjectService(IDbContextFactory conte .ToResult(activity!.Map()); })); - public async ValueTask> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(projectIdHash); @@ -283,8 +283,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(risks.Select(i => i.Map()).ToArray()); })); - public async ValueTask CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -309,8 +309,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -328,8 +328,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled.")); })); - public async ValueTask> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(programmeIdHash); @@ -344,8 +344,8 @@ public sealed class ProjectService(IDbContextFactory conte .ToResult(programme!.Map()); })); - public async ValueTask> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -358,8 +358,8 @@ public sealed class ProjectService(IDbContextFactory conte return Result.Ok(programmes.Select(i => i.Map()).ToArray()); })); - public async ValueTask> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { var decodeResult = hashService.DecodeLongIdHash(programmeIdHash); diff --git a/PostFundManagement.Application/Services/Shared/AuditService.cs b/PostFundManagement.Application/Services/Shared/AuditService.cs index 5104493..25ce543 100644 --- a/PostFundManagement.Application/Services/Shared/AuditService.cs +++ b/PostFundManagement.Application/Services/Shared/AuditService.cs @@ -6,8 +6,8 @@ namespace PostFundManagement.Application.Services.Shared; public sealed class AuditService(IDbContextFactory contextFactory, ITenantContextService tenantContextService) : IService { - public async ValueTask LogActionAsync(string entityId, string entityName, string action, string[] changes, Guid performedBy, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task LogActionAsync(string entityId, string entityName, string action, string[] changes, Guid performedBy, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); diff --git a/PostFundManagement.Application/Services/Shared/BrowserLocalStorageService.cs b/PostFundManagement.Application/Services/Shared/BrowserLocalStorageService.cs index 54cf872..981e343 100644 --- a/PostFundManagement.Application/Services/Shared/BrowserLocalStorageService.cs +++ b/PostFundManagement.Application/Services/Shared/BrowserLocalStorageService.cs @@ -4,32 +4,32 @@ namespace PostFundManagement.Application.Services.Shared; public sealed class BrowserLocalStorageService(ProtectedLocalStorage storage) { - public async ValueTask DeleteAsync(string key) => - await Result.Try((Func>)(async () => + public async Task DeleteAsync(string key) => + await Result.Try((Func>)(async () => { await storage.DeleteAsync(key); return Result.Ok(); })); - public async ValueTask SaveAsync(string key, string value) => - await Result.Try((Func>)(async () => + public async Task SaveAsync(string key, string value) => + await Result.Try((Func>)(async () => { await storage.SetAsync(key, value); return Result.Ok(); })); - public async ValueTask SaveAsync(string key, TValue value) where TValue : class => - await Result.Try((Func>)(async () => + public async Task SaveAsync(string key, TValue value) where TValue : class => + await Result.Try((Func>)(async () => { await storage.SetAsync(key, value); return Result.Ok(); })); - public async ValueTask> GetAsync(string key) => - await Result.Try((Func>>)(async () => + public async Task> GetAsync(string key) => + await Result.Try((Func>>)(async () => { var retrieval = await storage.GetAsync(key); @@ -38,8 +38,8 @@ public sealed class BrowserLocalStorageService(ProtectedLocalStorage storage) .ToResult(retrieval.Value!); })); - public async ValueTask> GetAsync(string key) where TValue : class => - await Result.Try((Func>>)(async () => + public async Task> GetAsync(string key) where TValue : class => + await Result.Try((Func>>)(async () => { var retrieval = await storage.GetAsync(key); diff --git a/PostFundManagement.Application/Services/Shared/EmailService.cs b/PostFundManagement.Application/Services/Shared/EmailService.cs index 4d00835..fb5badd 100644 --- a/PostFundManagement.Application/Services/Shared/EmailService.cs +++ b/PostFundManagement.Application/Services/Shared/EmailService.cs @@ -17,7 +17,7 @@ public sealed class EmailService(IOptions options) : IDisposable public EmailStatuses Status { get; private set; } = EmailStatuses.Disconnected; - public async ValueTask> SendEmailAsync(Message message, CancellationToken cancellationToken = default) + public async Task> SendEmailAsync(Message message, CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Send"); @@ -121,7 +121,7 @@ public sealed class EmailService(IOptions options) : IDisposable return Result.Fail(response); } - public async ValueTask> ConnectAsync(CancellationToken cancellationToken = default) + public async Task> ConnectAsync(CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Connect"); activity?.SetTag("email.smtp.connect", settings.Host); @@ -174,7 +174,7 @@ public sealed class EmailService(IOptions options) : IDisposable } } - public async ValueTask DisconnectAsync(CancellationToken cancellationToken = default) + public async Task DisconnectAsync(CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Disconnect"); activity?.SetTag("email.smtp.disconnect", settings.Host); diff --git a/PostFundManagement.Application/Services/Shared/HashService.cs b/PostFundManagement.Application/Services/Shared/HashService.cs index f3d63a3..ca489d2 100644 --- a/PostFundManagement.Application/Services/Shared/HashService.cs +++ b/PostFundManagement.Application/Services/Shared/HashService.cs @@ -67,7 +67,7 @@ public sealed partial class HashService(IHashids hasher) : IService } public async Task> DecodeHexHash(string hex) => - await Result.Try((Func>>)(async () => + await Result.Try((Func>>)(async () => { string decoded = hasher.DecodeHex(hex); diff --git a/PostFundManagement.Application/Services/Shared/IdentityCacheService.cs b/PostFundManagement.Application/Services/Shared/IdentityCacheService.cs index d69eb62..5a92865 100644 --- a/PostFundManagement.Application/Services/Shared/IdentityCacheService.cs +++ b/PostFundManagement.Application/Services/Shared/IdentityCacheService.cs @@ -5,6 +5,6 @@ namespace PostFundManagement.Application.Services.Shared; public sealed class IdentityCacheService(IdentityService service) : ServiceCacheBase, IIdentityService { - public async ValueTask UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default) => + public async Task UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default) => await service.UserIsAuthorisedAsync(userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Shared/IdentityService.cs b/PostFundManagement.Application/Services/Shared/IdentityService.cs index e2d59ac..cfe216d 100644 --- a/PostFundManagement.Application/Services/Shared/IdentityService.cs +++ b/PostFundManagement.Application/Services/Shared/IdentityService.cs @@ -7,8 +7,8 @@ namespace PostFundManagement.Application.Services.Shared; public sealed class IdentityService(IDbContextFactory contextFactory) : IIdentityService { - public async ValueTask UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); diff --git a/PostFundManagement.Application/Services/Shared/TokenService.cs b/PostFundManagement.Application/Services/Shared/TokenService.cs index ccae082..aab6d3c 100644 --- a/PostFundManagement.Application/Services/Shared/TokenService.cs +++ b/PostFundManagement.Application/Services/Shared/TokenService.cs @@ -9,8 +9,8 @@ public sealed class TokenService(ISecurityConnectApi connectApi, IOptions> GenerateAsync(CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GenerateAsync(CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { using var response = await connectApi.GetToken(new Domain.Security.Models.TokenRequest { @@ -34,7 +34,7 @@ public sealed class TokenService(ISecurityConnectApi connectApi, IOptions>>)(async () => + return await Result.Try((Func>>)(async () => { var errorResult = JsonSerializer.Deserialize(contentRaw); diff --git a/PostFundManagement.Application/Services/Tools/ToolCacheService.cs b/PostFundManagement.Application/Services/Tools/ToolCacheService.cs index 92b0ef0..22a3b3f 100644 --- a/PostFundManagement.Application/Services/Tools/ToolCacheService.cs +++ b/PostFundManagement.Application/Services/Tools/ToolCacheService.cs @@ -8,8 +8,8 @@ namespace PostFundManagement.Application.Services.Tools; public sealed class ToolCacheService(ToolService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IToolService { - public async ValueTask> EncodeLong(long id, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => + public async Task> EncodeLong(long id, CancellationToken cancellationToken) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.EncodeLong(id, cancellationToken); diff --git a/PostFundManagement.Application/Services/Tools/ToolService.cs b/PostFundManagement.Application/Services/Tools/ToolService.cs index 4b3aacb..b6a494f 100644 --- a/PostFundManagement.Application/Services/Tools/ToolService.cs +++ b/PostFundManagement.Application/Services/Tools/ToolService.cs @@ -5,6 +5,6 @@ namespace PostFundManagement.Application.Services.Tools; public sealed class ToolService(HashService service) : IToolService { - public ValueTask> EncodeLong(long id, CancellationToken cancellationToken = default) => - ValueTask.FromResult(service.HashEncodeLongId(id)); + public Task> EncodeLong(long id, CancellationToken cancellationToken = default) => + Task.FromResult(service.HashEncodeLongId(id)); } diff --git a/PostFundManagement.Application/Services/Users/UserCacheService.cs b/PostFundManagement.Application/Services/Users/UserCacheService.cs index a5d3ac3..00a9bfd 100644 --- a/PostFundManagement.Application/Services/Users/UserCacheService.cs +++ b/PostFundManagement.Application/Services/Users/UserCacheService.cs @@ -3,6 +3,7 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -11,8 +12,8 @@ namespace PostFundManagement.Application.Services.Users; public sealed class UserCacheService(UserService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IUserService { - public async ValueTask> GetUserAsync(Guid userId, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => + public async Task> GetUserAsync(Guid userId, CancellationToken cancellationToken) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetUserAsync(userId, cancellationToken); @@ -35,8 +36,8 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach return Result.Ok(cached.FromJson()); })); - public async ValueTask> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { if (!await featureManager.IsEnabledAsync(ServiceCachingFeature)) return await service.GetUsersAsync(pagination, request, cancellationToken); @@ -61,9 +62,11 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach return Result.Ok(cached.FromJson()); })); - public async ValueTask SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) => + [Audit] + public async Task SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) => await service.SyncUserAsync(userId, email, cancellationToken); - public async ValueTask UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) => + [Audit] + public async Task UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) => await service.UpdateUserStatusAsync(userId, status, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Users/UserService.cs b/PostFundManagement.Application/Services/Users/UserService.cs index 05efdf5..a156c37 100644 --- a/PostFundManagement.Application/Services/Users/UserService.cs +++ b/PostFundManagement.Application/Services/Users/UserService.cs @@ -9,8 +9,8 @@ namespace PostFundManagement.Application.Services.Users; public sealed class UserService(IDbContextFactory contextFactory) : IUserService { - public async ValueTask SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -35,8 +35,8 @@ public sealed class UserService(IDbContextFactory contextF return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); })); - public async ValueTask> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default) => - await Result.Try((Func>>)(async () => + public async Task> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -65,8 +65,8 @@ public sealed class UserService(IDbContextFactory contextF return Result.Ok(users.Select(u => u.Map()).ToArray()); })); - public async ValueTask UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) => - await Result.Try((Func>)(async () => + public async Task UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) => + await Result.Try((Func>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); @@ -77,8 +77,8 @@ public sealed class UserService(IDbContextFactory contextF return Result.OkIf(changedTotal > 0, UpdateError.New); })); - public async ValueTask> GetUserAsync(Guid userId, CancellationToken cancellationToken) => - await Result.Try((Func>>)(async () => + public async Task> GetUserAsync(Guid userId, CancellationToken cancellationToken) => + await Result.Try((Func>>)(async () => { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); diff --git a/PostFundManagement.Domain/Abstractions/IS3Service.cs b/PostFundManagement.Domain/Abstractions/IS3Service.cs index 89d00ba..2cee9f2 100644 --- a/PostFundManagement.Domain/Abstractions/IS3Service.cs +++ b/PostFundManagement.Domain/Abstractions/IS3Service.cs @@ -3,7 +3,7 @@ namespace PostFundManagement.Domain.Abstractions; public interface IS3Service { string GenerateCdnUrl(string fileKey); - ValueTask> GeneratePresignedUrlAsync(string fileKey, TimeSpan duration, string? contentType = null, CancellationToken cancellationToken = default); - ValueTask> UploadFileAsync(string fileName, string subPath, Stream fileStream, string contentType, CancellationToken cancellationToken = default); - ValueTask DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default); + Task> GeneratePresignedUrlAsync(string fileKey, TimeSpan duration, string? contentType = null, CancellationToken cancellationToken = default); + Task> UploadFileAsync(string fileName, string subPath, Stream fileStream, string contentType, CancellationToken cancellationToken = default); + Task DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/PostFundManagement.Domain/Entities/AuditLog.cs b/PostFundManagement.Domain/Entities/AuditLog.cs index 7d8d833..fc61504 100644 --- a/PostFundManagement.Domain/Entities/AuditLog.cs +++ b/PostFundManagement.Domain/Entities/AuditLog.cs @@ -3,12 +3,7 @@ using static PostFundManagement.Domain.Extensions.Constants; namespace PostFundManagement.Domain.Entities; [EntityTypeConfiguration] -public class AuditLog : Models.AuditLog -{ - public virtual Tenant? Tenant { get; set; } - - public virtual User? Performer { get; set; } -} +public class AuditLog : Models.AuditLog; public sealed class AuditLogConfiguration : IEntityTypeConfiguration { @@ -24,17 +19,5 @@ public sealed class AuditLogConfiguration : IEntityTypeConfiguration builder.Property(f => f.Changes).IsRequired(false).HasColumnType("jsonb"); builder.Property(f => f.PerformedBy).IsRequired(); builder.Property(f => f.Timestamp).IsRequired().HasDefaultValueSql("now()"); - - builder.HasOne(f => f.Tenant) - .WithMany() - .HasForeignKey(fk => fk.TenantId) - .IsRequired(false) - .OnDelete(DeleteBehavior.NoAction); - - builder.HasOne(f => f.Performer) - .WithMany() - .HasForeignKey(fk => fk.PerformedBy) - .IsRequired() - .OnDelete(DeleteBehavior.Restrict); } } \ No newline at end of file diff --git a/PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.Designer.cs b/PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.Designer.cs new file mode 100644 index 0000000..945a040 --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.Designer.cs @@ -0,0 +1,2224 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PostFundManagement.Infrastructure.Database; + +#nullable disable + +namespace PostFundManagement.Infrastructure.Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260922033954_RemovedRelationshipsFromAuditLog")] + partial class RemovedRelationshipsFromAuditLog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("PerformedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Activities", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.PrimitiveCollection("Changes") + .HasColumnType("jsonb"); + + b.Property("EntityId") + .IsRequired() + .HasColumnType("text"); + + b.Property("EntityName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PerformedBy") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Awards", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Beneficiary", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Location") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("ReachedCount") + .HasColumnType("integer"); + + b.Property("TargetCount") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("Beneficiaries", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Budget", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Budgets", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ChangeRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ApprovedBy") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Justification") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("RevisedBudget") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("RevisedEndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("ChangeRequests", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ComplianceItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Requirements") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VerifiedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("VerifiedBy"); + + b.ToTable("ComplianceItems", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("EffectiveAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalSignatureId") + .HasColumnType("text"); + + b.Property("SignedDocumentUrl") + .HasColumnType("text"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TotalValue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("AwardId") + .IsUnique(); + + b.HasIndex("CreatedBy"); + + b.ToTable("Contracts", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Disbursement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Disbursements", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Evidence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActivityId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DocumentUrl") + .IsRequired() + .HasColumnType("text"); + + b.Property("IndicatorId") + .HasColumnType("bigint"); + + b.Property("MilestoneId") + .HasColumnType("bigint"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("9"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.Property("Version") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.HasKey("Id"); + + b.HasIndex("ActivityId"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("IndicatorId"); + + b.HasIndex("MilestoneId"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Evidences", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Indicator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ActualAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("BaselineAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("TargetAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("UnitOfMeasure") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Indicators", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Instrument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.ToTable("Instruments", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Invite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("bigint"); + + b.Property("ContractId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("InvitedOrganisationId") + .HasColumnType("bigint"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Recipient") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AwardId"); + + b.HasIndex("ContractId"); + + b.HasIndex("InvitedOrganisationId"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("Recipient"); + + b.ToTable("Invites", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Issue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssignedTo") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Resolution") + .IsRequired() + .HasColumnType("text"); + + b.Property("ResolvedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("AssignedTo"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Issues", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Milestone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DueAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Milestones", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Destination") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Error") + .HasColumnType("text"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Platform") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Recipient") + .HasColumnType("uuid"); + + b.Property("SentAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Subject") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("Recipient"); + + b.HasIndex("TenantId"); + + b.ToTable("Notifications", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RegistrationNo") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("5"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Organisations", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Outcome", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BeneficiaryId") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("BeneficiaryId"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Outcomes", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("OwnedBy") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("OwnedBy"); + + b.HasIndex("TenantId"); + + b.ToTable("Portfolios", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("OwnedBy") + .HasColumnType("uuid"); + + b.Property("PortfolioId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OwnedBy"); + + b.HasIndex("PortfolioId"); + + b.HasIndex("TenantId"); + + b.ToTable("Programmes", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("EndedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("ProjectType") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("10"); + + b.Property("Sector") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("11"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Projects", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Risk", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("Impact") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("Likelihood") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("3"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("Risks", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Rule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ProgrammeId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.Property("Version") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProgrammeId"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Rules", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.SiteVisit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Findings") + .HasMaxLength(4096) + .HasColumnType("character varying(4096)"); + + b.Property("InspectorName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("VisitedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("ProjectId"); + + b.ToTable("SiteVisits", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedBy") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("UpdatedBy"); + + b.ToTable("Tenants", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("OrganisationId") + .HasColumnType("bigint"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("TenantId"); + + b.ToTable("TenantOrganisations", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("1"); + + b.Property("TenantId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("TenantId"); + + b.HasIndex("UserId"); + + b.ToTable("TenantUsers", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("LastLoginAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValueSql("2"); + + b.HasKey("Id"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Awards") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany("Awards") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Programme"); + + b.Navigation("Project"); + + b.Navigation("Tenant"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Beneficiary", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Budget", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ChangeRequest", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") + .WithMany() + .HasForeignKey("ApprovedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Approver"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.ComplianceItem", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Verifier") + .WithMany() + .HasForeignKey("VerifiedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("Verifier"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Contract", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Award", "Award") + .WithOne("Contract") + .HasForeignKey("PostFundManagement.Domain.Entities.Contract", "AwardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Award"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Disbursement", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Evidence", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Activity", "Activity") + .WithMany("Evidences") + .HasForeignKey("ActivityId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Indicator", "Indicator") + .WithMany() + .HasForeignKey("IndicatorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Milestone", "Milestone") + .WithMany() + .HasForeignKey("MilestoneId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Activity"); + + b.Navigation("Creator"); + + b.Navigation("Indicator"); + + b.Navigation("Milestone"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Indicator", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Instrument", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Instruments") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Invite", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Award", "Award") + .WithMany() + .HasForeignKey("AwardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Contract", "Contract") + .WithMany() + .HasForeignKey("ContractId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "InvitedOrganisation") + .WithMany() + .HasForeignKey("InvitedOrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "RecipientUser") + .WithMany() + .HasForeignKey("Recipient") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Award"); + + b.Navigation("Contract"); + + b.Navigation("InvitedOrganisation"); + + b.Navigation("Organisation"); + + b.Navigation("RecipientUser"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Issue", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Assignee") + .WithMany() + .HasForeignKey("AssignedTo") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Assignee"); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Milestone", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Project"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Notification", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "RecipientUser") + .WithMany() + .HasForeignKey("Recipient") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organisation"); + + b.Navigation("RecipientUser"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Organisation", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Outcome", b => + { + b.HasOne("PostFundManagement.Domain.Entities.Beneficiary", "Beneficiary") + .WithMany() + .HasForeignKey("BeneficiaryId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Beneficiary"); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Owner"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Portfolio", "Portfolio") + .WithMany("Programmes") + .HasForeignKey("PortfolioId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Owner"); + + b.Navigation("Portfolio"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Projects") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + + b.Navigation("Tenant"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Risk", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Rule", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Programme", "Programme") + .WithMany("Rules") + .HasForeignKey("ProgrammeId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Creator"); + + b.Navigation("Programme"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.SiteVisit", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Tenant", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "Updator") + .WithMany() + .HasForeignKey("UpdatedBy") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Creator"); + + b.Navigation("Updator"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantOrganisation", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Organisation"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.TenantUser", b => + { + b.HasOne("PostFundManagement.Domain.Entities.User", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") + .WithMany() + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("PostFundManagement.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Tenant"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Activity", b => + { + b.Navigation("Evidences"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => + { + b.Navigation("Contract"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Portfolio", b => + { + b.Navigation("Programmes"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Programme", b => + { + b.Navigation("Awards"); + + b.Navigation("Instruments"); + + b.Navigation("Projects"); + + b.Navigation("Rules"); + }); + + modelBuilder.Entity("PostFundManagement.Domain.Entities.Project", b => + { + b.Navigation("Awards"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.cs b/PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.cs new file mode 100644 index 0000000..6917c8c --- /dev/null +++ b/PostFundManagement.Infrastructure/Database/Migrations/20260922033954_RemovedRelationshipsFromAuditLog.cs @@ -0,0 +1,59 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PostFundManagement.Infrastructure.Database.Migrations +{ + /// + public partial class RemovedRelationshipsFromAuditLog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AuditLogs_Tenants_TenantId", + table: "AuditLogs"); + + migrationBuilder.DropForeignKey( + name: "FK_AuditLogs_Users_PerformedBy", + table: "AuditLogs"); + + migrationBuilder.DropIndex( + name: "IX_AuditLogs_PerformedBy", + table: "AuditLogs"); + + migrationBuilder.DropIndex( + name: "IX_AuditLogs_TenantId", + table: "AuditLogs"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_AuditLogs_PerformedBy", + table: "AuditLogs", + column: "PerformedBy"); + + migrationBuilder.CreateIndex( + name: "IX_AuditLogs_TenantId", + table: "AuditLogs", + column: "TenantId"); + + migrationBuilder.AddForeignKey( + name: "FK_AuditLogs_Tenants_TenantId", + table: "AuditLogs", + column: "TenantId", + principalTable: "Tenants", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_AuditLogs_Users_PerformedBy", + table: "AuditLogs", + column: "PerformedBy", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs b/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs index 36cd197..81a90d7 100644 --- a/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/PostFundManagement.Infrastructure/Database/Migrations/ApplicationDbContextModelSnapshot.cs @@ -133,10 +133,6 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.HasKey("Id"); - b.HasIndex("PerformedBy"); - - b.HasIndex("TenantId"); - b.ToTable("AuditLogs", (string)null); }); @@ -1495,24 +1491,6 @@ namespace PostFundManagement.Infrastructure.Database.Migrations b.Navigation("Updator"); }); - modelBuilder.Entity("PostFundManagement.Domain.Entities.AuditLog", b => - { - b.HasOne("PostFundManagement.Domain.Entities.User", "Performer") - .WithMany() - .HasForeignKey("PerformedBy") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("PostFundManagement.Domain.Entities.Tenant", "Tenant") - .WithMany() - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.NoAction); - - b.Navigation("Performer"); - - b.Navigation("Tenant"); - }); - modelBuilder.Entity("PostFundManagement.Domain.Entities.Award", b => { b.HasOne("PostFundManagement.Domain.Entities.User", "Approver") From 3cc8514136f539aa943b0d6784dc0e7d51e13ffc Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 22 Sep 2026 06:09:51 +0200 Subject: [PATCH 4/4] Added automatic audit feature --- .../Abstractions/Services/ICommunicationsService.cs | 4 +++- .../Abstractions/Services/IEvidenceService.cs | 10 ++++++++-- .../Abstractions/Services/IExecutionService.cs | 6 ++++++ .../Abstractions/Services/IGovernanceService.cs | 5 +++++ .../Abstractions/Services/IGrantsService.cs | 7 +++++++ .../Abstractions/Services/IProjectService.cs | 8 ++++++++ .../Abstractions/Services/IUserService.cs | 3 +++ PostFundManagement.Application/Extensions/Quartz.cs | 4 ++-- PostFundManagement.Application/Extensions/Services.cs | 4 +++- .../Communications/CommunicationsCacheService.cs | 4 +--- .../Services/Evidence/EvidenceCacheService.cs | 6 ------ .../Services/Execution/ExecutionCacheService.cs | 6 ------ .../Services/Governance/GovernanceCacheService.cs | 5 ----- .../Services/Grants/GrantsCacheService.cs | 7 ------- .../Services/Projects/ProjectCacheService.cs | 8 -------- .../Services/Users/UserCacheService.cs | 5 +---- PostFundManagement.Domain/Extensions/Constants.cs | 1 + 17 files changed, 48 insertions(+), 45 deletions(-) diff --git a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs index 238f392..2be4ff7 100644 --- a/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs +++ b/PostFundManagement.Application/Abstractions/Services/ICommunicationsService.cs @@ -1,11 +1,13 @@ using PostFundManagement.Application.Services.Communications; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface ICommunicationsService -{ +{ + [Audit] Task> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs b/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs index 59ce49b..7684c48 100644 --- a/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IEvidenceService.cs @@ -1,21 +1,26 @@ using PostFundManagement.Application.Services.Evidence; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IEvidenceService -{ +{ + [Audit] Task CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default); - + + [Audit] Task RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default); Task> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default); Task> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default); + [Audit] Task CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken); @@ -26,6 +31,7 @@ public interface IEvidenceService Task> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default); + [Audit] Task UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs b/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs index d611c66..0e02687 100644 --- a/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IExecutionService.cs @@ -1,5 +1,6 @@ using PostFundManagement.Application.Services.Execution; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; @@ -10,17 +11,22 @@ public interface IExecutionService Task> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default); + [Audit] Task CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default); Task> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default); Task> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default); + [Audit] Task UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default); + [Audit] Task UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs index 08039ec..29bd9c9 100644 --- a/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IGovernanceService.cs @@ -1,5 +1,6 @@ using PostFundManagement.Application.Services.Governance; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; @@ -18,12 +19,16 @@ public interface IGovernanceService Task> GetDashboardAsync(CancellationToken cancellationToken = default); + [Audit] Task CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default); + [Audit] Task CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs b/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs index b7e0556..8bc9460 100644 --- a/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IGrantsService.cs @@ -1,5 +1,6 @@ using PostFundManagement.Application.Services.Grants; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; @@ -18,17 +19,23 @@ public interface IGrantsService Task> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default); + [Audit] Task ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default); + [Audit] Task UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default); + [Audit] Task UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default); } diff --git a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs index 4656f3d..aa84ea8 100644 --- a/PostFundManagement.Application/Abstractions/Services/IProjectService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IProjectService.cs @@ -1,11 +1,13 @@ using PostFundManagement.Application.Services.Projects; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IProjectService { + [Audit] Task UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default); @@ -14,8 +16,10 @@ public interface IProjectService Task> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default); + [Audit] Task CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default); @@ -26,16 +30,20 @@ public interface IProjectService Task> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default); + [Audit] Task UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default); Task> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default); + [Audit] Task CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); + [Audit] Task UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default); Task> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default); diff --git a/PostFundManagement.Application/Abstractions/Services/IUserService.cs b/PostFundManagement.Application/Abstractions/Services/IUserService.cs index e9f2da8..585330b 100644 --- a/PostFundManagement.Application/Abstractions/Services/IUserService.cs +++ b/PostFundManagement.Application/Abstractions/Services/IUserService.cs @@ -1,15 +1,18 @@ using PostFundManagement.Application.Services.Users; using PostFundManagement.Domain; +using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; namespace PostFundManagement.Application.Abstractions.Services; public interface IUserService { + [Audit] Task SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default); Task> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default); + [Audit] Task UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default); Task> GetUserAsync(Guid userId, CancellationToken cancellationToken); diff --git a/PostFundManagement.Application/Extensions/Quartz.cs b/PostFundManagement.Application/Extensions/Quartz.cs index 31e8e51..8ae2777 100644 --- a/PostFundManagement.Application/Extensions/Quartz.cs +++ b/PostFundManagement.Application/Extensions/Quartz.cs @@ -8,7 +8,7 @@ public static class Quartz { public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration) { - var connectionString = configuration.GetConnectionString(DatabaseConfigName); + var connectionString = configuration.GetConnectionString(SchedulerDatabaseConfigName); services.Configure(options => { @@ -44,7 +44,7 @@ public static class Quartz public static IServiceCollection AddQuartzScheduler(this IServiceCollection services, string schedulerName, IConfiguration configuration) { - var connectionString = configuration.GetConnectionString(DatabaseConfigName); + var connectionString = configuration.GetConnectionString(SchedulerDatabaseConfigName); services.Configure(options => { diff --git a/PostFundManagement.Application/Extensions/Services.cs b/PostFundManagement.Application/Extensions/Services.cs index 127a317..df53b93 100644 --- a/PostFundManagement.Application/Extensions/Services.cs +++ b/PostFundManagement.Application/Extensions/Services.cs @@ -64,12 +64,14 @@ public static class Services services.AddAuditedCachedScoped(); services.AddAuditedCachedScoped(); services.AddAuditedCachedScoped(); - services.AddAuditedCachedScoped(); services.AddAuditedCachedScoped(); services.AddAuditedCachedScoped(); services.AddAuditedCachedScoped(); services.AddAuditedCachedScoped(); + services.AddScoped(); + services.Decorate(); + services.AddScoped(); services.Decorate(); diff --git a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs index 303ff65..a3a4a60 100644 --- a/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs +++ b/PostFundManagement.Application/Services/Communications/CommunicationsCacheService.cs @@ -3,7 +3,6 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -81,8 +80,7 @@ public sealed class CommunicationsCacheService(CommunicationsService service, ID return Result.Ok(cached.FromJson()); })); - - [Audit] + public async Task> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.SendInviteAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs b/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs index 8aea3fb..be3787b 100644 --- a/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs +++ b/PostFundManagement.Application/Services/Evidence/EvidenceCacheService.cs @@ -3,7 +3,6 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -12,15 +11,12 @@ namespace PostFundManagement.Application.Services.Evidence; public sealed class EvidenceCacheService(EvidenceService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IEvidenceService { - [Audit] public async Task CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default) => await service.CreateEvidenceAsync(userId, form, file, cancellationToken); - [Audit] public async Task CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateIndicatorAsync(request, userId, cancellationToken); - [Audit] public async Task CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateMilestoneAsync(request, userId, cancellationToken); @@ -192,11 +188,9 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa return Result.Ok(cached.FromJson()); })); - [Audit] public async Task RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) => await service.RecordIndicatorActualAmountAsync(indicatorIdHash, amount, userId, cancellationToken); - [Audit] public async Task UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateEvidenceAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs b/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs index c15cb68..c7c426d 100644 --- a/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs +++ b/PostFundManagement.Application/Services/Execution/ExecutionCacheService.cs @@ -3,7 +3,6 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -12,15 +11,12 @@ namespace PostFundManagement.Application.Services.Execution; public sealed class ExecutionCacheService(ExecutionService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IExecutionService { - [Audit] public async Task ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) => await service.ApproveDisbursementAsync(disbursementIdHash, userId, cancellationToken); - [Audit] public async Task CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateDisbursementAsync(request, userId, cancellationToken); - [Audit] public async Task CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateRiskAsync(request, userId, cancellationToken); @@ -120,11 +116,9 @@ public sealed class ExecutionCacheService(ExecutionService service, IDistributed return Result.Ok(cached.FromJson()); })); - [Audit] public async Task UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateDisbursementAsync(request, userId, cancellationToken); - [Audit] public async Task UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) => await service.UpdateRiskAsync(request, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs index 8c6e154..cc47ae3 100644 --- a/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs +++ b/PostFundManagement.Application/Services/Governance/GovernanceCacheService.cs @@ -3,7 +3,6 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -12,11 +11,9 @@ namespace PostFundManagement.Application.Services.Governance; public sealed class GovernanceCacheService(GovernanceService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IGovernanceService { - [Audit] public async Task CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateOrganisationAsync(request, userId, cancellationToken); - [Audit] public async Task CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreatePortfolioAsync(request, userId, cancellationToken); @@ -212,11 +209,9 @@ public sealed class GovernanceCacheService(GovernanceService service, IDistribut return Result.Ok(cached.FromJson()); })); - [Audit] public async Task> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) => await service.UpdateOrganisationAsync(request, cancellationToken); - [Audit] public async Task UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdatePortfolioAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs b/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs index c002711..102fbde 100644 --- a/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs +++ b/PostFundManagement.Application/Services/Grants/GrantsCacheService.cs @@ -3,7 +3,6 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -12,19 +11,15 @@ namespace PostFundManagement.Application.Services.Grants; public sealed class GrantsCacheService(GrantsService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IGrantsService { - [Audit] public async Task CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateAwardAsync(request, userId, cancellationToken); - [Audit] public async Task CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateBudgetAsync(request, userId, cancellationToken); - [Audit] public async Task CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateContractAsync(request, userId, cancellationToken); - [Audit] public async Task ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.ExecuteContractAsync(request, userId, cancellationToken); @@ -196,11 +191,9 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache return Result.Ok(cached!); })); - [Audit] public async Task UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default) => await service.UpdateContractAsync(request, cancellationToken); - [Audit] public async Task UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default) => await service.UploadContractAsync(contractIdHash, userId, file, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs index e19e611..85e05b9 100644 --- a/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs +++ b/PostFundManagement.Application/Services/Projects/ProjectCacheService.cs @@ -3,7 +3,6 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -12,15 +11,12 @@ namespace PostFundManagement.Application.Services.Projects; public sealed class ProjectCacheService(ProjectService service, IDistributedCache cache, IFeatureManager featureManager) : ServiceCacheBase, IProjectService { - [Audit] public async Task CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateActivityAsync(request, userId, cancellationToken); - [Audit] public async Task CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateProgrammeAsync(request, userId, cancellationToken); - [Audit] public async Task CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.CreateProjectAsync(request, userId, cancellationToken); @@ -312,19 +308,15 @@ public sealed class ProjectCacheService(ProjectService service, IDistributedCach return Result.Ok(cached.FromJson()); })); - [Audit] public async Task UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateMilestoneAsync(request, userId, cancellationToken); - [Audit] public async Task UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProgrammeAsync(request, userId, cancellationToken); - [Audit] public async Task UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProjectAsync(request, userId, cancellationToken); - [Audit] public async Task UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) => await service.UpdateProjectStatusAsync(request, userId, cancellationToken); } diff --git a/PostFundManagement.Application/Services/Users/UserCacheService.cs b/PostFundManagement.Application/Services/Users/UserCacheService.cs index 00a9bfd..74e877f 100644 --- a/PostFundManagement.Application/Services/Users/UserCacheService.cs +++ b/PostFundManagement.Application/Services/Users/UserCacheService.cs @@ -3,7 +3,6 @@ using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Shared; using PostFundManagement.Domain; -using PostFundManagement.Domain.Attributes; using PostFundManagement.Domain.Models; using static PostFundManagement.Domain.Extensions.Constants; @@ -62,11 +61,9 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach return Result.Ok(cached.FromJson()); })); - [Audit] public async Task SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) => await service.SyncUserAsync(userId, email, cancellationToken); - - [Audit] + public async Task UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default) => await service.UpdateUserStatusAsync(userId, status, cancellationToken); } diff --git a/PostFundManagement.Domain/Extensions/Constants.cs b/PostFundManagement.Domain/Extensions/Constants.cs index 7bd0bd0..20ba049 100644 --- a/PostFundManagement.Domain/Extensions/Constants.cs +++ b/PostFundManagement.Domain/Extensions/Constants.cs @@ -25,6 +25,7 @@ public static class Constants public const string GeneralBucketName = "pfm"; public const string DatabaseConfigName = "PfmDatabase"; + public const string SchedulerDatabaseConfigName = "PfmSchedulerDatabase"; public const int LabelLength = 256;