This commit is contained in:
Chris Chen
2026-06-20 17:51:33 -07:00
parent f55807fa7d
commit 3558c67fd7
55 changed files with 3140 additions and 85 deletions
+74
View File
@@ -19,6 +19,9 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
public DbSet<ExpenseSubCategory> ExpenseSubCategories => Set<ExpenseSubCategory>();
public DbSet<Expense> Expenses => Set<Expense>();
public DbSet<MonthlyStatement> MonthlyStatements => Set<MonthlyStatement>();
public DbSet<ChurchProfile> ChurchProfiles => Set<ChurchProfile>();
public DbSet<Check> Checks => Set<Check>();
public DbSet<CheckLine> CheckLines => Set<CheckLine>();
protected override void OnModelCreating(ModelBuilder builder)
{
@@ -210,6 +213,77 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
.HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull);
});
// ── ChurchProfile (singleton settings) ───────────────────────────────
builder.Entity<ChurchProfile>(entity =>
{
entity.Property(e => e.Name).HasMaxLength(200).IsRequired();
entity.Property(e => e.Address).HasMaxLength(500);
entity.Property(e => e.City).HasMaxLength(100);
entity.Property(e => e.State).HasMaxLength(50);
entity.Property(e => e.ZipCode).HasMaxLength(20);
entity.Property(e => e.BankName).HasMaxLength(200);
entity.Property(e => e.BankAccountNumber).HasMaxLength(50);
entity.Property(e => e.BankRoutingNumber).HasMaxLength(50);
entity.Property(e => e.CreatedBy).HasMaxLength(450);
entity.Property(e => e.UpdatedBy).HasMaxLength(450);
// Optimistic-concurrency token for safe check-number allocation.
entity.Property(e => e.xmin).IsRowVersion();
});
// ── Check (disbursement) ─────────────────────────────────────────────
builder.Entity<Check>(entity =>
{
entity.HasQueryFilter(c => !c.IsDeleted);
entity.Property(e => e.CheckNumber).HasMaxLength(50).IsRequired();
entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
entity.Property(e => e.PayeeType).HasMaxLength(20).IsRequired();
entity.Property(e => e.PayeeName).HasMaxLength(200).IsRequired();
entity.Property(e => e.PayeeAddress).HasMaxLength(500);
entity.Property(e => e.PayeeCity).HasMaxLength(100);
entity.Property(e => e.PayeeState).HasMaxLength(50);
entity.Property(e => e.PayeeZip).HasMaxLength(20);
entity.Property(e => e.Status).HasMaxLength(20).HasDefaultValue("Issued");
entity.Property(e => e.Memo).HasMaxLength(500);
entity.Property(e => e.IssuedBy).HasMaxLength(450).IsRequired();
entity.Property(e => e.VoidReason).HasMaxLength(500);
entity.Property(e => e.VoidedBy).HasMaxLength(450);
entity.Property(e => e.ReceiptSignatureBlobPath).HasMaxLength(500);
entity.Property(e => e.ReceiptSignedName).HasMaxLength(200);
entity.Property(e => e.ReceiptCapturedBy).HasMaxLength(450);
entity.Property(e => e.CreatedBy).HasMaxLength(450);
entity.Property(e => e.UpdatedBy).HasMaxLength(450);
entity.Property(e => e.DeletedBy).HasMaxLength(450);
// Unique check number among non-deleted rows.
entity.HasIndex(e => e.CheckNumber).IsUnique().HasFilter("\"IsDeleted\" = false");
entity.HasIndex(e => e.Status).HasFilter("\"IsDeleted\" = false");
entity.HasIndex(e => e.CheckDate);
entity.HasOne(e => e.Member).WithMany()
.HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull);
});
// ── CheckLine ────────────────────────────────────────────────────────
builder.Entity<CheckLine>(entity =>
{
// Mirror the parent Check's soft-delete filter (required relationship).
entity.HasQueryFilter(l => !l.Check!.IsDeleted);
entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
entity.Property(e => e.Description).HasMaxLength(500).IsRequired();
entity.Property(e => e.CreatedBy).HasMaxLength(450);
entity.Property(e => e.UpdatedBy).HasMaxLength(450);
entity.HasIndex(e => e.CheckId);
entity.HasIndex(e => e.ExpenseId);
entity.HasOne(e => e.Check).WithMany(c => c.Lines)
.HasForeignKey(e => e.CheckId).OnDelete(DeleteBehavior.Cascade);
entity.HasOne(e => e.Expense).WithMany()
.HasForeignKey(e => e.ExpenseId).OnDelete(DeleteBehavior.Restrict);
});
// ── MonthlyStatement ─────────────────────────────────────────────────
builder.Entity<MonthlyStatement>(entity =>
{
+17
View File
@@ -130,6 +130,22 @@ public static class DbSeeder
await db.SaveChangesAsync();
}
public static async Task SeedChurchProfileAsync(AppDbContext db)
{
// Singleton row used by the disbursement module (issuer info + check counter).
if (!await db.ChurchProfiles.AnyAsync())
{
db.ChurchProfiles.Add(new ChurchProfile
{
Name = "River Of Life Christian Church",
City = "Arcadia",
State = "CA",
NextCheckNumber = 1001,
});
await db.SaveChangesAsync();
}
}
/// <summary>
/// Seeds roles and (in Development) the default admin account.
/// Called once on application startup after migrations have been applied.
@@ -146,6 +162,7 @@ public static class DbSeeder
await SeedGivingCategoriesAsync(db);
await SeedMinistriesAsync(db);
await SeedExpenseCategoriesAsync(db);
await SeedChurchProfileAsync(db);
if (env.IsDevelopment())
await SeedAdminUserAsync(userManager);