Phase C: Add Manual Journal Entries (double-entry GL)
- JournalEntry + JournalEntryLine entities with Draft/Posted/Reversed lifecycle - JournalEntryStatus enum (Draft, Posted, Reversed) - Migration AddJournalEntries: two new tables with self-referencing reversal FK - IUnitOfWork/UnitOfWork wired with JournalEntries + JournalEntryLines repos - ApplicationDbContext: DbSets, tenant query filters, reversal FK config - LedgerService: JE lines added as 10th source in GetAccountLedgerAsync and ComputePriorBalanceAsync - JournalEntriesController: Index (All/Draft/Posted tabs), Create, Details, Post, Reverse, Delete - Views: Index, Create (dynamic balanced line grid with running debit/credit totals), Details - journal-entry-create.js: dynamic line management with balance indicator - Nav: Journal Entries added to Finance section in _Layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -324,6 +324,11 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDataPro
|
||||
/// <summary>Ad-hoc expense records (non-bill spending); tenant-filtered with soft delete.</summary>
|
||||
public DbSet<Expense> Expenses { get; set; }
|
||||
|
||||
/// <summary>Manual double-entry journal entries (Draft/Posted/Reversed lifecycle); tenant-filtered with soft delete.</summary>
|
||||
public DbSet<JournalEntry> JournalEntries { get; set; }
|
||||
/// <summary>Individual debit/credit lines within a journal entry; soft-delete only (access controlled through parent JournalEntry).</summary>
|
||||
public DbSet<JournalEntryLine> JournalEntryLines { get; set; }
|
||||
|
||||
// Job Templates
|
||||
/// <summary>Reusable job templates that pre-populate job items, coats, and prep services on job creation.</summary>
|
||||
public DbSet<JobTemplate> JobTemplates { get; set; }
|
||||
@@ -614,6 +619,11 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDataPro
|
||||
modelBuilder.Entity<Expense>().HasQueryFilter(e =>
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
|
||||
// Journal Entries: tenant-filtered; lines use soft-delete only (child rows)
|
||||
modelBuilder.Entity<JournalEntry>().HasQueryFilter(e =>
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
modelBuilder.Entity<JournalEntryLine>().HasQueryFilter(e => !e.IsDeleted);
|
||||
|
||||
// Purchase Orders
|
||||
modelBuilder.Entity<PurchaseOrder>().HasQueryFilter(e =>
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
@@ -633,6 +643,13 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDataPro
|
||||
.HasForeignKey(a => a.ParentAccountId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// JournalEntry self-referencing reversal link
|
||||
modelBuilder.Entity<JournalEntry>()
|
||||
.HasOne(je => je.ReversalOf)
|
||||
.WithMany()
|
||||
.HasForeignKey(je => je.ReversalOfId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// Vendor → DefaultExpenseAccount (no cascade)
|
||||
modelBuilder.Entity<Vendor>()
|
||||
.HasOne(s => s.DefaultExpenseAccount)
|
||||
|
||||
Reference in New Issue
Block a user