From 999f8a80f9007b686531dd2d6b105c4b910e02d0 Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Thu, 28 May 2026 16:16:24 -0700 Subject: [PATCH] feat(giving): add GivingCategory, OfferingSession, Giving entities + EF config Co-Authored-By: Claude Sonnet 4.6 --- API/ROLAC.API/Data/AppDbContext.cs | 53 +++++++++++++++++++++++ API/ROLAC.API/Entities/Giving.cs | 23 ++++++++++ API/ROLAC.API/Entities/GivingCategory.cs | 14 ++++++ API/ROLAC.API/Entities/OfferingSession.cs | 21 +++++++++ 4 files changed, 111 insertions(+) create mode 100644 API/ROLAC.API/Entities/Giving.cs create mode 100644 API/ROLAC.API/Entities/GivingCategory.cs create mode 100644 API/ROLAC.API/Entities/OfferingSession.cs diff --git a/API/ROLAC.API/Data/AppDbContext.cs b/API/ROLAC.API/Data/AppDbContext.cs index 60ff785..f82bd04 100644 --- a/API/ROLAC.API/Data/AppDbContext.cs +++ b/API/ROLAC.API/Data/AppDbContext.cs @@ -11,6 +11,9 @@ public class AppDbContext : IdentityDbContext public DbSet RefreshTokens => Set(); public DbSet Members => Set(); public DbSet FamilyUnits => Set(); + public DbSet GivingCategories => Set(); + public DbSet OfferingSessions => Set(); + public DbSet Givings => Set(); protected override void OnModelCreating(ModelBuilder builder) { @@ -89,5 +92,55 @@ public class AppDbContext : IdentityDbContext entity.HasOne(e => e.FamilyUnit).WithMany() .HasForeignKey(e => e.FamilyUnitId).OnDelete(DeleteBehavior.SetNull); }); + + // ── GivingCategory ─────────────────────────────────────────────────── + builder.Entity(entity => + { + entity.Property(e => e.Name_en).HasMaxLength(200).IsRequired(); + entity.Property(e => e.Name_zh).HasMaxLength(200); + entity.Property(e => e.Description_en).HasMaxLength(500); + entity.Property(e => e.Description_zh).HasMaxLength(500); + entity.Property(e => e.CreatedBy).HasMaxLength(450); + entity.Property(e => e.UpdatedBy).HasMaxLength(450); + }); + + // ── OfferingSession ────────────────────────────────────────────────── + builder.Entity(entity => + { + entity.Property(e => e.Status).HasMaxLength(20).HasDefaultValue("Draft"); + entity.Property(e => e.CashTotal).HasColumnType("decimal(18,2)"); + entity.Property(e => e.CheckTotal).HasColumnType("decimal(18,2)"); + entity.Property(e => e.SystemTotal).HasColumnType("decimal(18,2)"); + entity.Property(e => e.Difference).HasColumnType("decimal(18,2)"); + entity.Property(e => e.SubmittedBy).HasMaxLength(450); + entity.Property(e => e.ReconciledBy).HasMaxLength(450); + entity.Property(e => e.CreatedBy).HasMaxLength(450); + entity.Property(e => e.UpdatedBy).HasMaxLength(450); + entity.HasIndex(e => e.SessionDate).IsUnique(); + }); + + // ── Giving ─────────────────────────────────────────────────────────── + builder.Entity(entity => + { + entity.Property(e => e.Amount).HasColumnType("decimal(18,2)"); + entity.Property(e => e.PaymentMethod).HasMaxLength(20).IsRequired(); + entity.Property(e => e.CheckNumber).HasMaxLength(50); + entity.Property(e => e.ZelleReferenceCode).HasMaxLength(100); + entity.Property(e => e.PayPalTransactionId).HasMaxLength(100); + entity.Property(e => e.Notes).HasMaxLength(500); + entity.Property(e => e.CreatedBy).HasMaxLength(450); + entity.Property(e => e.UpdatedBy).HasMaxLength(450); + + entity.HasIndex(e => new { e.MemberId, e.GivingDate }); + entity.HasIndex(e => e.OfferingSessionId).HasFilter("\"OfferingSessionId\" IS NOT NULL"); + entity.HasIndex(e => e.GivingDate); + + entity.HasOne(e => e.GivingCategory).WithMany() + .HasForeignKey(e => e.GivingCategoryId).OnDelete(DeleteBehavior.Restrict); + entity.HasOne(e => e.Member).WithMany() + .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull); + entity.HasOne(e => e.OfferingSession).WithMany(s => s.Givings) + .HasForeignKey(e => e.OfferingSessionId).OnDelete(DeleteBehavior.Cascade); + }); } } diff --git a/API/ROLAC.API/Entities/Giving.cs b/API/ROLAC.API/Entities/Giving.cs new file mode 100644 index 0000000..32ad486 --- /dev/null +++ b/API/ROLAC.API/Entities/Giving.cs @@ -0,0 +1,23 @@ +using ROLAC.API.Entities.Base; + +namespace ROLAC.API.Entities; + +public class Giving : AuditableEntity +{ + public int Id { get; set; } + public int? MemberId { get; set; } + public int GivingCategoryId { get; set; } + public int? OfferingSessionId { get; set; } + public decimal Amount { get; set; } + public string PaymentMethod { get; set; } = "Cash"; // Cash|Check|Zelle|PayPal|Other + public string? CheckNumber { get; set; } + public string? ZelleReferenceCode { get; set; } + public string? PayPalTransactionId{ get; set; } + public DateOnly GivingDate { get; set; } + public bool IsAnonymous { get; set; } + public string? Notes { get; set; } + + public Member? Member { get; set; } + public GivingCategory? GivingCategory { get; set; } + public OfferingSession? OfferingSession { get; set; } +} diff --git a/API/ROLAC.API/Entities/GivingCategory.cs b/API/ROLAC.API/Entities/GivingCategory.cs new file mode 100644 index 0000000..234ba02 --- /dev/null +++ b/API/ROLAC.API/Entities/GivingCategory.cs @@ -0,0 +1,14 @@ +using ROLAC.API.Entities.Base; + +namespace ROLAC.API.Entities; + +public class GivingCategory : AuditableEntity +{ + public int Id { get; set; } + public string Name_en { get; set; } = null!; + public string? Name_zh { get; set; } + public string? Description_en { get; set; } + public string? Description_zh { get; set; } + public bool IsActive { get; set; } = true; + public int SortOrder { get; set; } +} diff --git a/API/ROLAC.API/Entities/OfferingSession.cs b/API/ROLAC.API/Entities/OfferingSession.cs new file mode 100644 index 0000000..460e89e --- /dev/null +++ b/API/ROLAC.API/Entities/OfferingSession.cs @@ -0,0 +1,21 @@ +using ROLAC.API.Entities.Base; + +namespace ROLAC.API.Entities; + +public class OfferingSession : AuditableEntity +{ + public int Id { get; set; } + public DateOnly SessionDate { get; set; } + public string Status { get; set; } = "Draft"; // Draft | Submitted | Reconciled + public decimal CashTotal { get; set; } + public decimal CheckTotal { get; set; } + public decimal SystemTotal { get; set; } + public decimal Difference { get; set; } + public string? Notes { get; set; } + public DateTimeOffset? SubmittedAt { get; set; } + public string? SubmittedBy { get; set; } + public DateTimeOffset? ReconciledAt { get; set; } + public string? ReconciledBy { get; set; } + + public List Givings { get; set; } = []; +}