1cb7a8ca4a
Phase 3 — eliminated ApplicationDbContext from all non-exempt controllers, routing all data access through IUnitOfWork. Added IPlainRepository<T> for the four platform entities (Announcement, BannedIp, DashboardTip, ReleaseNote) that intentionally don't extend BaseEntity and therefore can't use the constrained IRepository<T>. Added permanent-exception comments to the 18 controllers that legitimately retain direct DbContext access (Identity infra, cross-tenant platform ops, bulk streaming exports). Phase 4 — added EnforceDataAccessArchitecture() to Program.cs, a startup gate that reflects over every Controller subclass and throws at boot if any non-exempt controller injects ApplicationDbContext. The app cannot start with a violation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
106 lines
4.0 KiB
C#
106 lines
4.0 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using PowderCoating.Core.Entities;
|
|
using PowderCoating.Core.Enums;
|
|
using PowderCoating.Core.Interfaces.Repositories;
|
|
using PowderCoating.Infrastructure.Data;
|
|
|
|
namespace PowderCoating.Infrastructure.Repositories;
|
|
|
|
/// <summary>
|
|
/// Typed repository for <see cref="Bill"/> that provides domain-specific multi-level
|
|
/// include queries previously expressed inline in <c>BillsController</c>.
|
|
/// </summary>
|
|
public class BillRepository : Repository<Bill>, IBillRepository
|
|
{
|
|
public BillRepository(ApplicationDbContext context) : base(context) { }
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<Bill?> LoadForViewAsync(int id)
|
|
{
|
|
return await _context.Bills
|
|
.Where(b => b.Id == id && !b.IsDeleted)
|
|
.Include(b => b.Vendor)
|
|
.Include(b => b.APAccount)
|
|
.Include(b => b.LineItems.Where(li => !li.IsDeleted))
|
|
.ThenInclude(li => li.Account)
|
|
.Include(b => b.LineItems.Where(li => !li.IsDeleted))
|
|
.ThenInclude(li => li.Job)
|
|
.Include(b => b.Payments.Where(p => !p.IsDeleted))
|
|
.ThenInclude(p => p.BankAccount)
|
|
.FirstOrDefaultAsync();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<Bill?> LoadForEditAsync(int id)
|
|
{
|
|
return await _context.Bills
|
|
.Where(b => b.Id == id && !b.IsDeleted)
|
|
.Include(b => b.LineItems.Where(li => !li.IsDeleted))
|
|
.FirstOrDefaultAsync();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<List<Bill>> GetForIndexAsync(string? statusFilter, string? searchTerm, decimal? searchAmount)
|
|
{
|
|
var query = _context.Bills
|
|
.Include(b => b.Vendor)
|
|
.Include(b => b.LineItems.Where(li => !li.IsDeleted))
|
|
.Where(b => !b.IsDeleted);
|
|
|
|
if (statusFilter == "Unpaid")
|
|
query = query.Where(b => b.Status == BillStatus.Open || b.Status == BillStatus.PartiallyPaid);
|
|
else if (statusFilter == "Overdue")
|
|
query = query.Where(b => b.Status != BillStatus.Paid && b.Status != BillStatus.Voided &&
|
|
b.DueDate.HasValue && b.DueDate.Value.Date < DateTime.Today);
|
|
|
|
if (!string.IsNullOrEmpty(searchTerm))
|
|
{
|
|
var term = searchTerm;
|
|
query = query.Where(b =>
|
|
b.BillNumber.Contains(term) ||
|
|
b.Vendor.CompanyName.Contains(term) ||
|
|
(b.VendorInvoiceNumber != null && b.VendorInvoiceNumber.Contains(term)) ||
|
|
(b.Memo != null && b.Memo.Contains(term)) ||
|
|
b.LineItems.Any(li => li.Description.Contains(term)) ||
|
|
(searchAmount.HasValue && (b.Total == searchAmount.Value || b.AmountPaid == searchAmount.Value)));
|
|
}
|
|
|
|
return await query.OrderByDescending(b => b.BillDate).ToListAsync();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<string?> GetLastBillNumberAsync(string prefix)
|
|
{
|
|
return await _context.Bills
|
|
.IgnoreQueryFilters()
|
|
.Where(b => b.BillNumber.StartsWith(prefix))
|
|
.OrderByDescending(b => b.BillNumber)
|
|
.Select(b => b.BillNumber)
|
|
.FirstOrDefaultAsync();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<string?> GetLastPaymentNumberAsync(string prefix)
|
|
{
|
|
return await _context.BillPayments
|
|
.IgnoreQueryFilters()
|
|
.Where(p => p.PaymentNumber.StartsWith(prefix))
|
|
.OrderByDescending(p => p.PaymentNumber)
|
|
.Select(p => p.PaymentNumber)
|
|
.FirstOrDefaultAsync();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<List<Bill>> GetForDateRangeAsync(DateTime start, DateTime end)
|
|
{
|
|
return await _context.Bills
|
|
.Where(b => !b.IsDeleted && b.BillDate >= start && b.BillDate <= end)
|
|
.Include(b => b.Vendor)
|
|
.Include(b => b.LineItems.Where(li => !li.IsDeleted))
|
|
.ThenInclude(li => li.Account)
|
|
.Include(b => b.Payments.Where(p => !p.IsDeleted))
|
|
.OrderBy(b => b.BillDate)
|
|
.ToListAsync();
|
|
}
|
|
}
|