Add facility overhead (rent + utilities) to operating costs and pricing engine

Adds MonthlyRent, MonthlyUtilities, and MonthlyBillableHours to CompanyOperatingCosts so fixed shop occupancy costs are recovered on every quote. The pricing engine converts these into a per-hour rate and applies it as a transparent "Facility Overhead" line between oven batch cost and shop supplies. UI added in Company Settings Operating Costs tab and Setup Wizard Step 3; migration AddFacilityOverheadFields applied. Help docs and AI knowledge base updated to cover the new fields and the revised quote pricing calculation order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 19:35:00 -04:00
parent 813f76138c
commit 4153acf3aa
14 changed files with 9575 additions and 21 deletions
@@ -361,6 +361,57 @@
</div>
</div>
<!-- Facility Overhead -->
<h6 class="border-bottom pb-2 mb-3 mt-3">Facility Overhead
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="right"
data-bs-title="Facility Overhead"
data-bs-content="Enter your monthly shop rent and combined utility costs. The system divides these by your estimated billable hours to derive a per-hour overhead rate, which is then added to every quote proportionally to the estimated job time. This ensures fixed facility costs are recovered across all jobs rather than absorbed into your markup.">
<i class="bi bi-question-circle"></i>
</a>
</h6>
<div class="row align-items-end">
<div class="col-md-3">
<div class="mb-3">
<label for="monthlyRent" class="form-label">Monthly Rent</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" step="0.01" class="form-control facility-overhead-input" id="monthlyRent" name="MonthlyRent" value="@(Model.OperatingCosts?.MonthlyRent ?? 0)" min="0" max="1000000">
<span class="input-group-text">/mo</span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="monthlyUtilities" class="form-label">Monthly Utilities</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" step="0.01" class="form-control facility-overhead-input" id="monthlyUtilities" name="MonthlyUtilities" value="@(Model.OperatingCosts?.MonthlyUtilities ?? 0)" min="0" max="1000000">
<span class="input-group-text">/mo</span>
</div>
<small class="text-muted">Electricity, gas, water, internet</small>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="monthlyBillableHours" class="form-label">Billable Hours/Month</label>
<input type="number" step="1" class="form-control facility-overhead-input" id="monthlyBillableHours" name="MonthlyBillableHours" value="@(Model.OperatingCosts?.MonthlyBillableHours ?? 160)" min="1" max="10000">
<small class="text-muted">Typical: 160 hrs (4 wks × 40 hrs)</small>
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label text-muted">Calculated Rate</label>
<div class="input-group">
<span class="input-group-text bg-light"><i class="bi bi-calculator"></i></span>
<input type="text" class="form-control bg-light" id="facilityOverheadRateDisplay" readonly
value="@((Model.OperatingCosts?.FacilityOverheadRatePerHour ?? 0).ToString("C2")) / hr">
</div>
<small class="text-muted">Added to quotes per estimated labor hour</small>
</div>
</div>
</div>
<!-- Equipment Operating Costs -->
<h6 class="border-bottom pb-2 mb-3 mt-3">Equipment Operating Costs
<a tabindex="0" class="help-icon" role="button"
@@ -2131,6 +2182,16 @@
});
// Operating Costs Form Submit
// Live facility overhead rate preview
function updateFacilityOverheadRate() {
var rent = parseFloat($('#monthlyRent').val()) || 0;
var utilities = parseFloat($('#monthlyUtilities').val()) || 0;
var hours = parseInt($('#monthlyBillableHours').val()) || 1;
var rate = hours > 0 ? (rent + utilities) / hours : 0;
$('#facilityOverheadRateDisplay').val('$' + rate.toFixed(2) + ' / hr');
}
$('.facility-overhead-input').on('input', updateFacilityOverheadRate);
$('#operatingCostsForm').on('submit', function (e) {
e.preventDefault();
@@ -2153,7 +2214,10 @@
ComplexitySimplePercent: parseFloat($('#complexitySimplePercent').val()) || 0,
ComplexityModeratePercent: parseFloat($('#complexityModeratePercent').val()) || 5,
ComplexityComplexPercent: parseFloat($('#complexityComplexPercent').val()) || 15,
ComplexityExtremePercent: parseFloat($('#complexityExtremePercent').val()) || 25
ComplexityExtremePercent: parseFloat($('#complexityExtremePercent').val()) || 25,
MonthlyRent: parseFloat($('#monthlyRent').val()) || 0,
MonthlyUtilities: parseFloat($('#monthlyUtilities').val()) || 0,
MonthlyBillableHours: parseInt($('#monthlyBillableHours').val()) || 160
};
const btn = $('#btnSaveOperatingCosts');