Fix catalog item pricing and rogue margin-points text

Catalog DefaultPrice is always the base price — removed the IncludePrepCost
gate that was adding prep service labor on top of catalog items. PrepServices
on catalog items exist for scheduling purposes only, not pricing.

Also fixed Razor syntax bug in Details.cshtml where @(expr).ToString("F1")
rendered the raw decimal followed by the literal string ".ToString("F1")"
instead of the formatted value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 16:43:42 -04:00
parent 810d5a5dc1
commit 9292f3169c
2 changed files with 3 additions and 11 deletions
@@ -391,16 +391,8 @@ public class PricingCalculationService : IPricingCalculationService
baseSubtotal = baseUnitPrice * item.Quantity; baseSubtotal = baseUnitPrice * item.Quantity;
_logger.LogInformation("Catalog base: ${Price} × {Qty} = ${Base}", baseUnitPrice, item.Quantity, baseSubtotal); _logger.LogInformation("Catalog base: ${Price} × {Qty} = ${Base}", baseUnitPrice, item.Quantity, baseSubtotal);
// Optionally add prep service labor cost for catalog items (opt-in toggle in wizard) // Catalog DefaultPrice is always the base — prep service labor is never added on top.
// AI items exclude prep cost — it's already baked into the AI estimate // PrepServices on catalog items are used for scheduling only, not pricing.
if (!item.IsAiItem && item.IncludePrepCost && item.PrepServices != null && item.PrepServices.Any())
{
var totalPrepMinutes = item.PrepServices.Sum(ps => ps.EstimatedMinutes);
var prepLaborCost = (totalPrepMinutes / 60m) * costs.StandardLaborRate;
baseSubtotal += prepLaborCost;
totalLaborCost += prepLaborCost;
_logger.LogInformation("Catalog item prep cost: {Min}min × ${Rate}/h = ${Cost}", totalPrepMinutes, costs.StandardLaborRate, prepLaborCost);
}
// Custom (non-inventory) powder coats must be purchased separately — add their material // Custom (non-inventory) powder coats must be purchased separately — add their material
// cost on top of the catalog base price. Inventory powder is assumed baked into DefaultPrice. // cost on top of the catalog base price. Inventory powder is assumed baked into DefaultPrice.
@@ -1230,7 +1230,7 @@
var priceBeforeDiscount = pb.Total + pb.DiscountAmount; var priceBeforeDiscount = pb.Total + pb.DiscountAmount;
var marginBeforeDiscount = priceBeforeDiscount > 0 ? ((priceBeforeDiscount - totalDirectCost) / priceBeforeDiscount * 100m) : 0m; var marginBeforeDiscount = priceBeforeDiscount > 0 ? ((priceBeforeDiscount - totalDirectCost) / priceBeforeDiscount * 100m) : 0m;
<div class="small text-muted"> <div class="small text-muted">
Without discount: @marginBeforeDiscount.ToString("F1")% — discount cost you @(marginBeforeDiscount - effectiveMargin).ToString("F1") margin points Without discount: @marginBeforeDiscount.ToString("F1")% — discount cost you @((marginBeforeDiscount - effectiveMargin).ToString("F1")) margin points
</div> </div>
} }
} }