Fix price override ignored for catalog items; fix time entry delete UI
- QuotePricingAssemblyService: catalog no-coat items now respect PowderCostOverride instead of always using DefaultPrice - PricingCalculationService: removed duplicate catalog fast-path in CalculateQuoteTotalsAsync that bypassed CalculateQuoteItemPriceAsync (and thus ignored PowderCostOverride); all items now go through the single authoritative path - Jobs/Details.cshtml: timeTracking.del() and timeTracking.save() now call costing.load() after success so the time entry row and costing breakdown stay in sync Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -590,53 +590,9 @@ public class PricingCalculationService : IPricingCalculationService
|
||||
{
|
||||
QuoteItemPricingResult itemResult;
|
||||
|
||||
// Catalog items - if they have coats, add coat costs to catalog base price
|
||||
if (item.CatalogItemId.HasValue)
|
||||
{
|
||||
var catalogItem = await _unitOfWork.CatalogItems.GetByIdAsync(item.CatalogItemId.Value);
|
||||
if (catalogItem != null)
|
||||
{
|
||||
// If the catalog item has coats, calculate using CalculateQuoteItemPriceAsync
|
||||
// (which already includes the catalog base price + coat costs)
|
||||
if (item.Coats != null && item.Coats.Any())
|
||||
{
|
||||
// CalculateQuoteItemPriceAsync already adds catalog base price to coat costs
|
||||
itemResult = await CalculateQuoteItemPriceAsync(item, companyId, ovenCostOverride);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No coats - use simple catalog default price
|
||||
var catalogItemTotal = catalogItem.DefaultPrice * item.Quantity;
|
||||
itemResult = new QuoteItemPricingResult
|
||||
{
|
||||
MaterialCost = 0,
|
||||
LaborCost = 0,
|
||||
EquipmentCost = 0,
|
||||
ItemSubtotal = catalogItemTotal,
|
||||
UnitPrice = catalogItem.DefaultPrice,
|
||||
TotalPrice = catalogItemTotal
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Catalog item not found, create zero result
|
||||
itemResult = new QuoteItemPricingResult
|
||||
{
|
||||
MaterialCost = 0,
|
||||
LaborCost = 0,
|
||||
EquipmentCost = 0,
|
||||
ItemSubtotal = 0,
|
||||
UnitPrice = 0,
|
||||
TotalPrice = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculated items use the full pricing calculation
|
||||
itemResult = await CalculateQuoteItemPriceAsync(item, companyId, ovenCostOverride);
|
||||
}
|
||||
// All items (catalog and calculated) go through CalculateQuoteItemPriceAsync, which
|
||||
// handles PowderCostOverride, prep cost inclusion, and all item type variants.
|
||||
itemResult = await CalculateQuoteItemPriceAsync(item, companyId, ovenCostOverride);
|
||||
|
||||
itemResults.Add(itemResult);
|
||||
}
|
||||
|
||||
@@ -104,8 +104,11 @@ public class QuotePricingAssemblyService : IQuotePricingAssemblyService
|
||||
var catalogItem = await _unitOfWork.CatalogItems.GetByIdAsync(itemDto.CatalogItemId.Value);
|
||||
if (catalogItem != null)
|
||||
{
|
||||
item.UnitPrice = catalogItem.DefaultPrice;
|
||||
item.TotalPrice = catalogItem.DefaultPrice * itemDto.Quantity;
|
||||
var unitPrice = itemDto.PowderCostOverride is > 0
|
||||
? itemDto.PowderCostOverride.Value
|
||||
: catalogItem.DefaultPrice;
|
||||
item.UnitPrice = unitPrice;
|
||||
item.TotalPrice = unitPrice * itemDto.Quantity;
|
||||
_logger.LogInformation("Catalog item no coats: UnitPrice={Unit}, TotalPrice={Total}", item.UnitPrice, item.TotalPrice);
|
||||
}
|
||||
|
||||
|
||||
@@ -2978,6 +2978,7 @@
|
||||
if (!r.ok) { const d = await r.json(); errEl.textContent = d.error ?? 'Save failed.'; errEl.classList.remove('d-none'); return; }
|
||||
modal.hide();
|
||||
await load();
|
||||
costing.load();
|
||||
showToast(id > 0 ? 'Time entry updated.' : 'Time logged.', 'success');
|
||||
} catch { errEl.textContent = 'Network error.'; errEl.classList.remove('d-none'); }
|
||||
}
|
||||
@@ -2990,7 +2991,7 @@
|
||||
headers: { 'Content-Type': 'application/json', 'RequestVerificationToken': tok },
|
||||
body: JSON.stringify({ id })
|
||||
});
|
||||
if (r.ok) { await load(); showToast('Time entry deleted.', 'secondary'); }
|
||||
if (r.ok) { await load(); costing.load(); showToast('Time entry deleted.', 'secondary'); }
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
|
||||
Reference in New Issue
Block a user