Merge hotfix/quote-stats-converted-mismatch: exclude Converted quotes from Quotes Index stat cards
This commit is contained in:
@@ -33,10 +33,17 @@ public interface IQuoteRepository : IRepository<Quote>
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns aggregate stat counts and total value for the Index view stat cards, scoped to the
|
/// Returns aggregate stat counts and total value for the Index view stat cards, scoped to the
|
||||||
/// current company by the global query filter. Pass status ID sets (derived from QuoteStatusLookup)
|
/// given <paramref name="companyId"/> (explicit predicate for defense in depth, in addition to
|
||||||
/// to classify open vs. approved/converted quotes.
|
/// the global query filter). Pass status ID sets (derived from QuoteStatusLookup) to classify
|
||||||
|
/// open vs. approved/converted quotes.
|
||||||
|
/// <para>
|
||||||
|
/// <paramref name="excludedStatusId"/> drops quotes in that status (Converted) from every card so
|
||||||
|
/// the stat strip reflects the same population as the default Index list, which hides Converted
|
||||||
|
/// quotes. Without this, a converted quote (which cannot be deleted while a job is linked) inflates
|
||||||
|
/// TotalValue and the approved count even though it never appears in the list.
|
||||||
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<QuoteIndexStats> GetIndexStatsAsync(List<int> openStatusIds, List<int> approvedConvertedStatusIds);
|
Task<QuoteIndexStats> GetIndexStatsAsync(int companyId, List<int> openStatusIds, List<int> approvedConvertedStatusIds, int? excludedStatusId = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads quote items with their coat passes (InventoryItem + Vendor) and prep services for
|
/// Loads quote items with their coat passes (InventoryItem + Vendor) and prep services for
|
||||||
|
|||||||
@@ -69,10 +69,21 @@ public class QuoteRepository : Repository<Quote>, IQuoteRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<QuoteIndexStats> GetIndexStatsAsync(List<int> openStatusIds, List<int> approvedConvertedStatusIds)
|
public async Task<QuoteIndexStats> GetIndexStatsAsync(int companyId, List<int> openStatusIds, List<int> approvedConvertedStatusIds, int? excludedStatusId = null)
|
||||||
{
|
{
|
||||||
var stats = await _context.Quotes
|
// Explicit CompanyId predicate (defense in depth) on top of the global tenant filter.
|
||||||
.Where(q => !q.IsDeleted)
|
var query = _context.Quotes
|
||||||
|
.Where(q => q.CompanyId == companyId && !q.IsDeleted);
|
||||||
|
|
||||||
|
// Exclude the same status the default Index list hides (Converted) so the stat strip and the
|
||||||
|
// visible list summarize the identical population — a converted quote left behind by a blocked
|
||||||
|
// delete must not inflate the cards while being invisible in the list.
|
||||||
|
if (excludedStatusId.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Where(q => q.QuoteStatusId != excludedStatusId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var stats = await query
|
||||||
.Select(q => new { q.QuoteStatusId, q.Total })
|
.Select(q => new { q.QuoteStatusId, q.Total })
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -248,7 +248,10 @@ public class QuotesController : Controller
|
|||||||
var approvedConvertedIds = quoteStatuses
|
var approvedConvertedIds = quoteStatuses
|
||||||
.Where(s => s.StatusCode == AppConstants.StatusCodes.Quote.Approved || s.StatusCode == AppConstants.StatusCodes.Quote.Converted)
|
.Where(s => s.StatusCode == AppConstants.StatusCodes.Quote.Approved || s.StatusCode == AppConstants.StatusCodes.Quote.Converted)
|
||||||
.Select(s => s.Id).ToList();
|
.Select(s => s.Id).ToList();
|
||||||
var indexStats = await _unitOfWork.Quotes.GetIndexStatsAsync(draftSentIds, approvedConvertedIds);
|
// Exclude Converted from the stat cards so they match the default list (which hides Converted).
|
||||||
|
var convertedStatusId = quoteStatuses
|
||||||
|
.FirstOrDefault(s => s.StatusCode == AppConstants.StatusCodes.Quote.Converted)?.Id;
|
||||||
|
var indexStats = await _unitOfWork.Quotes.GetIndexStatsAsync(companyId, draftSentIds, approvedConvertedIds, convertedStatusId);
|
||||||
ViewBag.StatOpenCount = indexStats.OpenCount;
|
ViewBag.StatOpenCount = indexStats.OpenCount;
|
||||||
ViewBag.StatApprovedCount = indexStats.ApprovedConvertedCount;
|
ViewBag.StatApprovedCount = indexStats.ApprovedConvertedCount;
|
||||||
ViewBag.StatTotalValue = indexStats.TotalValue;
|
ViewBag.StatTotalValue = indexStats.TotalValue;
|
||||||
|
|||||||
Reference in New Issue
Block a user