Phase D: Add Vendor Credits (AP cycle completion)
- VendorCredit, VendorCreditLineItem, VendorCreditApplication entities - VendorCreditStatus enum (Open, PartiallyApplied, Applied, Voided) - Migration AddVendorCredits: three new tables - IUnitOfWork/UnitOfWork wired with all three repositories - VendorCreditsController: Index (status tabs), Create, Details, Post, Apply, Void - Post action: DR AP, CR each expense line (reverses original expense) - Apply action: links credit to bill, updates Bill.AmountPaid and bill status - Views: Index (summary cards + table), Create (dynamic line grid), Details (apply panel) - Nav: Vendor Credits added to Finance section in _Layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -329,6 +329,13 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDataPro
|
||||
/// <summary>Individual debit/credit lines within a journal entry; soft-delete only (access controlled through parent JournalEntry).</summary>
|
||||
public DbSet<JournalEntryLine> JournalEntryLines { get; set; }
|
||||
|
||||
/// <summary>Credit notes received from vendors (returned goods, pricing disputes); tenant-filtered with soft delete.</summary>
|
||||
public DbSet<VendorCredit> VendorCredits { get; set; }
|
||||
/// <summary>Expense-reversal line items on a vendor credit; soft-delete only.</summary>
|
||||
public DbSet<VendorCreditLineItem> VendorCreditLineItems { get; set; }
|
||||
/// <summary>Application records linking a vendor credit to a specific bill; soft-delete only.</summary>
|
||||
public DbSet<VendorCreditApplication> VendorCreditApplications { 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; }
|
||||
@@ -624,6 +631,12 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDataPro
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
modelBuilder.Entity<JournalEntryLine>().HasQueryFilter(e => !e.IsDeleted);
|
||||
|
||||
// Vendor Credits: tenant-filtered; child rows soft-delete only
|
||||
modelBuilder.Entity<VendorCredit>().HasQueryFilter(e =>
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
modelBuilder.Entity<VendorCreditLineItem>().HasQueryFilter(e => !e.IsDeleted);
|
||||
modelBuilder.Entity<VendorCreditApplication>().HasQueryFilter(e => !e.IsDeleted);
|
||||
|
||||
// Purchase Orders
|
||||
modelBuilder.Entity<PurchaseOrder>().HasQueryFilter(e =>
|
||||
!e.IsDeleted && (IsPlatformAdmin || e.CompanyId == CurrentCompanyId));
|
||||
@@ -650,6 +663,20 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDataPro
|
||||
.HasForeignKey(je => je.ReversalOfId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// VendorCredit → APAccount (no cascade)
|
||||
modelBuilder.Entity<VendorCredit>()
|
||||
.HasOne(vc => vc.APAccount)
|
||||
.WithMany()
|
||||
.HasForeignKey(vc => vc.APAccountId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// VendorCreditLineItem → Account (nullable, no cascade)
|
||||
modelBuilder.Entity<VendorCreditLineItem>()
|
||||
.HasOne(li => li.Account)
|
||||
.WithMany()
|
||||
.HasForeignKey(li => li.AccountId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
// Vendor → DefaultExpenseAccount (no cascade)
|
||||
modelBuilder.Entity<Vendor>()
|
||||
.HasOne(s => s.DefaultExpenseAccount)
|
||||
|
||||
Reference in New Issue
Block a user