Fix NullReferenceException on Quote Details when quote total is zero

PricingBreakdown was only populated when quote.Total > 0, but the Details
view unconditionally dereferences PricingBreakdown.ItemsSubtotal. Sandblast-
only quotes can legitimately have a $0 total (no powder/oven costs), leaving
PricingBreakdown null and crashing the Details render.

Removed the Total > 0 guard from both Details action overloads — always
populate PricingBreakdown from the stored snapshot fields (all values are 0
for an unpriced or sandblast-only quote, which is safe for display).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 14:16:24 -04:00
parent 63a85b6ce9
commit 0054b7d108
@@ -365,33 +365,30 @@ public class QuotesController : Controller
}
// Build pricing breakdown from stored snapshot values — never recalculate on load
if (quote.Total > 0)
quoteDto.PricingBreakdown = new QuotePricingBreakdownDto
{
quoteDto.PricingBreakdown = new QuotePricingBreakdownDto
{
MaterialCosts = quote.MaterialCosts,
LaborCosts = quote.LaborCosts,
EquipmentCosts = quote.EquipmentCosts,
ItemsSubtotal = quote.ItemsSubtotal,
OvenBatchCost = quote.OvenBatchCost,
OvenBatches = quote.OvenBatches,
OvenCycleMinutes = quote.OvenCycleMinutes ?? 0,
ShopSuppliesAmount = quote.ShopSuppliesAmount,
ShopSuppliesPercent = quote.ShopSuppliesPercent,
OverheadCosts = quote.OverheadAmount,
OverheadPercent = quote.OverheadPercent,
ProfitMargin = quote.ProfitMargin,
ProfitPercent = quote.ProfitPercent,
SubtotalBeforeDiscount = quote.SubTotal,
DiscountAmount = quote.DiscountAmount,
DiscountPercent = quote.DiscountPercent,
SubtotalAfterDiscount = quote.SubTotal - quote.DiscountAmount,
RushFee = quote.RushFee,
TaxPercent = quote.TaxPercent,
TaxAmount = quote.TaxAmount,
Total = quote.Total
};
}
MaterialCosts = quote.MaterialCosts,
LaborCosts = quote.LaborCosts,
EquipmentCosts = quote.EquipmentCosts,
ItemsSubtotal = quote.ItemsSubtotal,
OvenBatchCost = quote.OvenBatchCost,
OvenBatches = quote.OvenBatches,
OvenCycleMinutes = quote.OvenCycleMinutes ?? 0,
ShopSuppliesAmount = quote.ShopSuppliesAmount,
ShopSuppliesPercent = quote.ShopSuppliesPercent,
OverheadCosts = quote.OverheadAmount,
OverheadPercent = quote.OverheadPercent,
ProfitMargin = quote.ProfitMargin,
ProfitPercent = quote.ProfitPercent,
SubtotalBeforeDiscount = quote.SubTotal,
DiscountAmount = quote.DiscountAmount,
DiscountPercent = quote.DiscountPercent,
SubtotalAfterDiscount = quote.SubTotal - quote.DiscountAmount,
RushFee = quote.RushFee,
TaxPercent = quote.TaxPercent,
TaxAmount = quote.TaxAmount,
Total = quote.Total
};
// Load change history
var changeHistories = await _unitOfWork.Quotes.GetChangeHistoryAsync(id.Value);
@@ -544,33 +541,30 @@ public class QuotesController : Controller
var currentUser = await _userManager.GetUserAsync(User);
// Populate pricing breakdown from stored snapshot values — never recalculate on load
if (quote.Total > 0)
quoteDto.PricingBreakdown = new QuotePricingBreakdownDto
{
quoteDto.PricingBreakdown = new QuotePricingBreakdownDto
{
MaterialCosts = quote.MaterialCosts,
LaborCosts = quote.LaborCosts,
EquipmentCosts = quote.EquipmentCosts,
ItemsSubtotal = quote.ItemsSubtotal,
OvenBatchCost = quote.OvenBatchCost,
OvenBatches = quote.OvenBatches,
OvenCycleMinutes = quote.OvenCycleMinutes ?? 0,
ShopSuppliesAmount = quote.ShopSuppliesAmount,
ShopSuppliesPercent = quote.ShopSuppliesPercent,
OverheadCosts = quote.OverheadAmount,
OverheadPercent = quote.OverheadPercent,
ProfitMargin = quote.ProfitMargin,
ProfitPercent = quote.ProfitPercent,
SubtotalBeforeDiscount = quote.SubTotal,
DiscountAmount = quote.DiscountAmount,
DiscountPercent = quote.DiscountPercent,
SubtotalAfterDiscount = quote.SubTotal - quote.DiscountAmount,
RushFee = quote.RushFee,
TaxPercent = quote.TaxPercent,
TaxAmount = quote.TaxAmount,
Total = quote.Total
};
}
MaterialCosts = quote.MaterialCosts,
LaborCosts = quote.LaborCosts,
EquipmentCosts = quote.EquipmentCosts,
ItemsSubtotal = quote.ItemsSubtotal,
OvenBatchCost = quote.OvenBatchCost,
OvenBatches = quote.OvenBatches,
OvenCycleMinutes = quote.OvenCycleMinutes ?? 0,
ShopSuppliesAmount = quote.ShopSuppliesAmount,
ShopSuppliesPercent = quote.ShopSuppliesPercent,
OverheadCosts = quote.OverheadAmount,
OverheadPercent = quote.OverheadPercent,
ProfitMargin = quote.ProfitMargin,
ProfitPercent = quote.ProfitPercent,
SubtotalBeforeDiscount = quote.SubTotal,
DiscountAmount = quote.DiscountAmount,
DiscountPercent = quote.DiscountPercent,
SubtotalAfterDiscount = quote.SubTotal - quote.DiscountAmount,
RushFee = quote.RushFee,
TaxPercent = quote.TaxPercent,
TaxAmount = quote.TaxAmount,
Total = quote.Total
};
if (currentUser?.CompanyId == null)
{
TempData["Error"] = "Company information not found.";