diff --git a/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs b/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs
index 2166558..85bc65c 100644
--- a/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs
+++ b/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs
@@ -65,6 +65,8 @@ public class SaveQuickQuoteRequest
public decimal EstimatedUnitPrice { get; set; }
public decimal MaterialCost { get; set; }
public decimal LaborCost { get; set; }
+ public decimal OvenBatchCost { get; set; }
+ public int OvenCycleMinutes { get; set; }
}
/// Internal JSON schema returned by Claude for quick quote analysis.
diff --git a/src/PowderCoating.Infrastructure/Services/AiQuickQuoteService.cs b/src/PowderCoating.Infrastructure/Services/AiQuickQuoteService.cs
index 23bdf40..d86f90e 100644
--- a/src/PowderCoating.Infrastructure/Services/AiQuickQuoteService.cs
+++ b/src/PowderCoating.Infrastructure/Services/AiQuickQuoteService.cs
@@ -257,9 +257,14 @@ When estimating from a verbal description:
subtotal = costs.ShopMinimumCharge;
var unitPrice = Math.Max(0, Math.Round(subtotal, 2));
- var total = unitPrice * request.Quantity;
var markupAmount = (materialCost + consumablesSurcharge) * (costs.GeneralMarkupPercentage / 100m);
- var ovenCycleMinutes = costs.DefaultOvenCycleMinutes > 0 ? costs.DefaultOvenCycleMinutes : 45;
+
+ // Oven batch charge: 1 batch, DefaultOvenCycleMinutes (fallback 50 min).
+ // Added at quote level (not baked into unitPrice) to match how the regular pricing engine works.
+ var ovenCycleMinutes = costs.DefaultOvenCycleMinutes > 0 ? costs.DefaultOvenCycleMinutes : 50;
+ var ovenBatchCost = Math.Round((ovenCycleMinutes / 60m) * costs.OvenOperatingCostPerHour, 2);
+
+ var total = unitPrice * request.Quantity + ovenBatchCost;
var breakdown = new AiPricingBreakdown
{
@@ -273,7 +278,7 @@ When estimating from a verbal description:
MinFloorApplied = false,
LaborCost = Math.Round(laborCost, 2),
OvenCycleMinutes = ovenCycleMinutes,
- OvenCost = 0m,
+ OvenCost = ovenBatchCost,
RequiresPreheat = ai.RequiresPreheat,
PreheatMinutes = preheatMinutes,
PreheatCost = Math.Round(preheatCost, 2),
diff --git a/src/PowderCoating.Web/Controllers/AiQuickQuoteController.cs b/src/PowderCoating.Web/Controllers/AiQuickQuoteController.cs
index e111049..465ae4e 100644
--- a/src/PowderCoating.Web/Controllers/AiQuickQuoteController.cs
+++ b/src/PowderCoating.Web/Controllers/AiQuickQuoteController.cs
@@ -107,6 +107,9 @@ public class AiQuickQuoteController : Controller
var quoteNumber = await GenerateQuoteNumberAsync(companyId);
var now = DateTime.UtcNow;
+ var itemsSubtotal = request.EstimatedUnitPrice * request.Quantity;
+ var ovenCycleMinutes = request.OvenCycleMinutes > 0 ? request.OvenCycleMinutes : 50;
+
var quote = new Quote
{
CompanyId = companyId,
@@ -121,12 +124,14 @@ public class AiQuickQuoteController : Controller
CustomerPO = request.Reference,
MaterialCosts = request.MaterialCost,
LaborCosts = request.LaborCost,
- ItemsSubtotal = request.EstimatedUnitPrice * request.Quantity,
- SubTotal = request.EstimatedUnitPrice * request.Quantity,
- Total = request.EstimatedUnitPrice * request.Quantity,
+ ItemsSubtotal = itemsSubtotal,
+ OvenBatches = 1,
+ OvenCycleMinutes = ovenCycleMinutes,
+ OvenBatchCost = request.OvenBatchCost,
+ SubTotal = itemsSubtotal,
+ Total = itemsSubtotal + request.OvenBatchCost,
TaxPercent = 0,
- TaxAmount = 0,
- OvenBatches = 1
+ TaxAmount = 0
};
if (draftStatus != null)
diff --git a/src/PowderCoating.Web/Views/Shared/_AiQuickQuoteWidget.cshtml b/src/PowderCoating.Web/Views/Shared/_AiQuickQuoteWidget.cshtml
index 44396f5..22efc29 100644
--- a/src/PowderCoating.Web/Views/Shared/_AiQuickQuoteWidget.cshtml
+++ b/src/PowderCoating.Web/Views/Shared/_AiQuickQuoteWidget.cshtml
@@ -86,6 +86,7 @@
Confidence
diff --git a/src/PowderCoating.Web/wwwroot/js/ai-quick-quote.js b/src/PowderCoating.Web/wwwroot/js/ai-quick-quote.js
index 344f28d..54301a8 100644
--- a/src/PowderCoating.Web/wwwroot/js/ai-quick-quote.js
+++ b/src/PowderCoating.Web/wwwroot/js/ai-quick-quote.js
@@ -29,6 +29,7 @@
resComplexity:document.getElementById('qq-res-complexity'),
resMinutes: document.getElementById('qq-res-minutes'),
resPrice: document.getElementById('qq-res-price'),
+ resOven: document.getElementById('qq-res-oven'),
resConfidence:document.getElementById('qq-res-confidence'),
resReasoning: document.getElementById('qq-res-reasoning'),
powderSection:document.getElementById('qq-powder-section'),
@@ -150,6 +151,15 @@
el.resComplexity.textContent = r.complexity || '—';
el.resMinutes.textContent = r.estimatedMinutes ? r.estimatedMinutes + ' min' : '—';
el.resPrice.textContent = formatCurrency(r.estimatedTotal || r.estimatedUnitPrice);
+
+ const ovenCost = r.breakdown?.ovenCost;
+ const ovenMin = r.breakdown?.ovenCycleMinutes;
+ if (ovenCost && ovenCost > 0) {
+ el.resOven.textContent = `incl. oven 1 batch ${ovenMin ? ovenMin + ' min' : ''}: ${formatCurrency(ovenCost)}`;
+ } else {
+ el.resOven.textContent = '';
+ }
+
el.resReasoning.textContent = r.reasoning || '';
// Confidence badge
@@ -220,7 +230,9 @@
coatCount: parseInt(el.coats.value, 10) || 1,
estimatedUnitPrice: lastResult.estimatedUnitPrice,
materialCost: lastResult.breakdown?.materialCost ?? 0,
- laborCost: lastResult.breakdown?.laborCost ?? 0
+ laborCost: lastResult.breakdown?.laborCost ?? 0,
+ ovenBatchCost: lastResult.breakdown?.ovenCost ?? 0,
+ ovenCycleMinutes: lastResult.breakdown?.ovenCycleMinutes ?? 50
};
const response = await post('/AiQuickQuote/Save', body);