From cf929557fe7593b78cf4ff0f1ff99b9b905da0f7 Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Fri, 29 May 2026 18:11:56 -0700 Subject: [PATCH] feat(expense): add Expense + MonthlyStatement entities and EF config --- API/ROLAC.API/Data/AppDbContext.cs | 52 ++++++++++++++++++++++ API/ROLAC.API/Entities/Expense.cs | 32 +++++++++++++ API/ROLAC.API/Entities/MonthlyStatement.cs | 20 +++++++++ 3 files changed, 104 insertions(+) create mode 100644 API/ROLAC.API/Entities/Expense.cs create mode 100644 API/ROLAC.API/Entities/MonthlyStatement.cs diff --git a/API/ROLAC.API/Data/AppDbContext.cs b/API/ROLAC.API/Data/AppDbContext.cs index ea6a837..04df4e4 100644 --- a/API/ROLAC.API/Data/AppDbContext.cs +++ b/API/ROLAC.API/Data/AppDbContext.cs @@ -17,6 +17,8 @@ public class AppDbContext : IdentityDbContext public DbSet Ministries => Set(); public DbSet ExpenseCategoryGroups => Set(); public DbSet ExpenseSubCategories => Set(); + public DbSet Expenses => Set(); + public DbSet MonthlyStatements => Set(); protected override void OnModelCreating(ModelBuilder builder) { @@ -172,5 +174,55 @@ public class AppDbContext : IdentityDbContext entity.HasOne(e => e.Group).WithMany(g => g.SubCategories) .HasForeignKey(e => e.GroupId).OnDelete(DeleteBehavior.Restrict); }); + + // ── Expense ────────────────────────────────────────────────────────── + builder.Entity(entity => + { + entity.HasQueryFilter(e => !e.IsDeleted); + + entity.Property(e => e.Type).HasMaxLength(30).IsRequired(); + entity.Property(e => e.Status).HasMaxLength(30).HasDefaultValue("Draft"); + entity.Property(e => e.Amount).HasColumnType("decimal(18,2)"); + entity.Property(e => e.Description).HasMaxLength(500).IsRequired(); + entity.Property(e => e.VendorName).HasMaxLength(200); + entity.Property(e => e.CheckNumber).HasMaxLength(50); + entity.Property(e => e.ReceiptBlobPath).HasMaxLength(500); + entity.Property(e => e.ReviewNotes).HasMaxLength(500); + entity.Property(e => e.SubmittedBy).HasMaxLength(450); + entity.Property(e => e.ReviewedBy).HasMaxLength(450); + entity.Property(e => e.PaidBy).HasMaxLength(450); + entity.Property(e => e.CreatedBy).HasMaxLength(450); + entity.Property(e => e.UpdatedBy).HasMaxLength(450); + entity.Property(e => e.DeletedBy).HasMaxLength(450); + + entity.HasIndex(e => e.MinistryId); + entity.HasIndex(e => e.Status).HasFilter("\"IsDeleted\" = false"); + entity.HasIndex(e => e.ExpenseDate); + + entity.HasOne(e => e.Ministry).WithMany() + .HasForeignKey(e => e.MinistryId).OnDelete(DeleteBehavior.Restrict); + entity.HasOne(e => e.CategoryGroup).WithMany() + .HasForeignKey(e => e.CategoryGroupId).OnDelete(DeleteBehavior.Restrict); + entity.HasOne(e => e.SubCategory).WithMany() + .HasForeignKey(e => e.SubCategoryId).OnDelete(DeleteBehavior.Restrict); + entity.HasOne(e => e.Member).WithMany() + .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull); + }); + + // ── MonthlyStatement ───────────────────────────────────────────────── + builder.Entity(entity => + { + entity.Property(e => e.OpeningBalance).HasColumnType("decimal(18,2)"); + entity.Property(e => e.TotalGiving).HasColumnType("decimal(18,2)"); + entity.Property(e => e.TotalOtherIncome).HasColumnType("decimal(18,2)"); + entity.Property(e => e.TotalExpenses).HasColumnType("decimal(18,2)"); + entity.Property(e => e.CalculatedClosingBalance).HasColumnType("decimal(18,2)"); + entity.Property(e => e.BankStatementBalance).HasColumnType("decimal(18,2)"); + entity.Property(e => e.Difference).HasColumnType("decimal(18,2)"); + entity.Property(e => e.FinalizedBy).HasMaxLength(450); + entity.Property(e => e.CreatedBy).HasMaxLength(450); + entity.Property(e => e.UpdatedBy).HasMaxLength(450); + entity.HasIndex(e => new { e.Year, e.Month }).IsUnique(); + }); } } diff --git a/API/ROLAC.API/Entities/Expense.cs b/API/ROLAC.API/Entities/Expense.cs new file mode 100644 index 0000000..c4f737e --- /dev/null +++ b/API/ROLAC.API/Entities/Expense.cs @@ -0,0 +1,32 @@ +using ROLAC.API.Entities.Base; +namespace ROLAC.API.Entities; + +public class Expense : SoftDeleteEntity +{ + public int Id { get; set; } + public int MinistryId { get; set; } + public int CategoryGroupId { get; set; } + public int SubCategoryId { get; set; } + public string Type { get; set; } = "StaffReimbursement"; // VendorPayment | StaffReimbursement + public string Status { get; set; } = "Draft"; // see state machine + public decimal Amount { get; set; } + public string Description { get; set; } = null!; + public string? VendorName { get; set; } + public int? MemberId { get; set; } + public string? CheckNumber { get; set; } + public DateOnly ExpenseDate { get; set; } + public string? ReceiptBlobPath { get; set; } + public string? Notes { get; set; } + public string? SubmittedBy { get; set; } + public DateTimeOffset? SubmittedAt { get; set; } + public string? ReviewedBy { get; set; } + public DateTimeOffset? ReviewedAt { get; set; } + public string? ReviewNotes { get; set; } + public DateTimeOffset? PaidAt { get; set; } + public string? PaidBy { get; set; } + + public Ministry? Ministry { get; set; } + public ExpenseCategoryGroup? CategoryGroup { get; set; } + public ExpenseSubCategory? SubCategory { get; set; } + public Member? Member { get; set; } +} diff --git a/API/ROLAC.API/Entities/MonthlyStatement.cs b/API/ROLAC.API/Entities/MonthlyStatement.cs new file mode 100644 index 0000000..a97bf4e --- /dev/null +++ b/API/ROLAC.API/Entities/MonthlyStatement.cs @@ -0,0 +1,20 @@ +using ROLAC.API.Entities.Base; +namespace ROLAC.API.Entities; + +public class MonthlyStatement : AuditableEntity +{ + public int Id { get; set; } + public int Year { get; set; } + public int Month { get; set; } + public decimal OpeningBalance { get; set; } + public decimal TotalGiving { get; set; } + public decimal TotalOtherIncome { get; set; } + public decimal TotalExpenses { get; set; } + public decimal CalculatedClosingBalance { get; set; } + public decimal BankStatementBalance { get; set; } + public decimal Difference { get; set; } + public string? Notes { get; set; } + public bool IsFinalized { get; set; } + public DateTimeOffset? FinalizedAt { get; set; } + public string? FinalizedBy { get; set; } +}