Add status-group pills to Invoices list, default to Unpaid
Bare /Invoices now redirects to statusGroup=unpaid (Draft, Sent, Overdue) so the list is immediately actionable. Four pills — All, Unpaid, Partial, Paid — mirror the Jobs page pattern with live badge counts. The existing status dropdown and outstanding/thisMonth flags are preserved for dashboard deep-links. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -82,14 +82,15 @@ public class InvoicesController : Controller
|
||||
// -----------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Displays the paginated invoice list with multi-mode filtering. The filter cascade handles
|
||||
/// nine combinations of overdue/outstanding/thisMonth flags with status and search term so the
|
||||
/// database receives a single targeted predicate — no full-table load then in-memory LINQ.
|
||||
/// statusGroup pills (unpaid/partial/paid/all) plus legacy flag combinations (overdue/outstanding/thisMonth)
|
||||
/// so the database receives a single targeted predicate — no full-table load then in-memory LINQ.
|
||||
/// Balance-due sort is computed in the ORDER BY expression rather than a stored column because
|
||||
/// balance = Total − AmountPaid − CreditApplied − GiftCertificateRedeemed changes on every payment.
|
||||
/// </summary>
|
||||
public async Task<IActionResult> Index(
|
||||
string? searchTerm,
|
||||
InvoiceStatus? statusFilter,
|
||||
string? statusGroup,
|
||||
string? sortColumn,
|
||||
string sortDirection = "desc",
|
||||
bool outstandingOnly = false,
|
||||
@@ -100,6 +101,11 @@ public class InvoicesController : Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
// Default landing: show unpaid invoices so the list is immediately actionable.
|
||||
if (string.IsNullOrEmpty(statusGroup) && !statusFilter.HasValue &&
|
||||
string.IsNullOrEmpty(searchTerm) && !outstandingOnly && !thisMonthOnly && !overdueOnly)
|
||||
return RedirectToAction("Index", new { statusGroup = "unpaid" });
|
||||
|
||||
var today = DateTime.Today;
|
||||
var startOfMonth = new DateTime(today.Year, today.Month, 1);
|
||||
var endOfMonth = startOfMonth.AddMonths(1);
|
||||
@@ -116,7 +122,18 @@ public class InvoicesController : Controller
|
||||
|
||||
System.Linq.Expressions.Expression<Func<Invoice, bool>>? filter = null;
|
||||
|
||||
if (overdueOnly)
|
||||
// Status-group pills take priority over the dropdown and legacy flags.
|
||||
if (!string.IsNullOrEmpty(statusGroup))
|
||||
{
|
||||
filter = statusGroup switch
|
||||
{
|
||||
"unpaid" => i => i.Status == InvoiceStatus.Draft || i.Status == InvoiceStatus.Sent || i.Status == InvoiceStatus.Overdue,
|
||||
"partial" => i => i.Status == InvoiceStatus.PartiallyPaid,
|
||||
"paid" => i => i.Status == InvoiceStatus.Paid,
|
||||
_ => null // "all" — no predicate
|
||||
};
|
||||
}
|
||||
else if (overdueOnly)
|
||||
{
|
||||
filter = i => (i.Status == InvoiceStatus.Sent || i.Status == InvoiceStatus.PartiallyPaid || i.Status == InvoiceStatus.Overdue)
|
||||
&& i.DueDate.HasValue && i.DueDate.Value < today;
|
||||
@@ -215,12 +232,20 @@ public class InvoicesController : Controller
|
||||
|
||||
ViewBag.SearchTerm = searchTerm;
|
||||
ViewBag.StatusFilter = statusFilter;
|
||||
ViewBag.StatusGroup = statusGroup;
|
||||
ViewBag.OutstandingOnly = outstandingOnly;
|
||||
ViewBag.ThisMonthOnly = thisMonthOnly;
|
||||
ViewBag.OverdueOnly = overdueOnly;
|
||||
ViewBag.SortColumn = gridRequest.SortColumn;
|
||||
ViewBag.SortDirection = gridRequest.SortDirection;
|
||||
|
||||
// Pill badge counts — always global (not scoped to current filter/page)
|
||||
ViewBag.UnpaidCount = await _unitOfWork.Invoices.CountAsync(i =>
|
||||
i.Status == InvoiceStatus.Draft || i.Status == InvoiceStatus.Sent || i.Status == InvoiceStatus.Overdue);
|
||||
ViewBag.PartialCount = await _unitOfWork.Invoices.CountAsync(i => i.Status == InvoiceStatus.PartiallyPaid);
|
||||
ViewBag.PaidCount = await _unitOfWork.Invoices.CountAsync(i => i.Status == InvoiceStatus.Paid);
|
||||
ViewBag.AllCount = await _unitOfWork.Invoices.CountAsync();
|
||||
|
||||
return View(pagedResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
Reference in New Issue
Block a user