From 0054b7d10811e4d0db8d679045490ae9b4620927 Mon Sep 17 00:00:00 2001 From: Scott Pouliot Date: Wed, 6 May 2026 14:16:24 -0400 Subject: [PATCH] Fix NullReferenceException on Quote Details when quote total is zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../Controllers/QuotesController.cs | 98 +++++++++---------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/src/PowderCoating.Web/Controllers/QuotesController.cs b/src/PowderCoating.Web/Controllers/QuotesController.cs index 610fff7..17e5d17 100644 --- a/src/PowderCoating.Web/Controllers/QuotesController.cs +++ b/src/PowderCoating.Web/Controllers/QuotesController.cs @@ -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.";