Files
PowderCoatingLogix/src/PowderCoating.Infrastructure/Repositories/BillRepository.cs
T
spouliot 1cb7a8ca4a Phases 3 & 4: Complete data access architecture migration
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>
2026-04-28 09:17:29 -04:00

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();
}
}