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
@@ -59,6 +59,49 @@
</div>
</div>
<!-- Facility Overhead -->
<div class="wizard-card">
<h5 class="wizard-card-title mb-3">Facility Overhead</h5>
<p class="text-secondary small mb-3">
Enter your monthly shop rent and utilities so the system can recover those costs proportionally
across every job. Leave at zero if you work from home or your facility costs are already factored
into your markup.
</p>
<div class="row g-3">
<div class="col-md-4">
<label asp-for="MonthlyRent" class="form-label fw-semibold"></label>
<div class="input-group">
<span class="input-group-text">$</span>
<input asp-for="MonthlyRent" class="form-control wz-overhead" step="0.01" type="number" min="0" />
<span class="input-group-text">/mo</span>
</div>
<div class="form-text">Your monthly lease or rent payment for the shop space.</div>
</div>
<div class="col-md-4">
<label asp-for="MonthlyUtilities" class="form-label fw-semibold"></label>
<div class="input-group">
<span class="input-group-text">$</span>
<input asp-for="MonthlyUtilities" class="form-control wz-overhead" step="0.01" type="number" min="0" />
<span class="input-group-text">/mo</span>
</div>
<div class="form-text">Combined electricity, gas, water, and internet.</div>
</div>
<div class="col-md-4">
<label asp-for="MonthlyBillableHours" class="form-label fw-semibold"></label>
<input asp-for="MonthlyBillableHours" class="form-control wz-overhead" step="1" type="number" min="1" />
<div class="form-text">Hours per month the shop is actively producing work. Default: 160 (4 wks × 40 hrs).</div>
</div>
<div class="col-md-4">
<label class="form-label text-muted">Calculated overhead rate</label>
<div class="input-group">
<span class="input-group-text bg-light"><i class="bi bi-calculator"></i></span>
<input type="text" id="wzOverheadRate" class="form-control bg-light" readonly value="$0.00 / hr">
</div>
<div class="form-text">This amount is added to quotes per estimated labor hour.</div>
</div>
</div>
</div>
<!-- Equipment Costs -->
<div class="wizard-card">
<h5 class="wizard-card-title mb-3">Equipment Costs (per hour) <button type="button" class="btn btn-link btn-sm p-0 text-primary fw-normal ms-1" data-bs-toggle="modal" data-bs-target="#equipCalcModal"><i class="bi bi-calculator me-1"></i>Help me calculate</button></h5>
@@ -336,6 +379,19 @@
@section Scripts {
<script>
// ── Facility Overhead Rate Preview ────────────────────────
function updateWzOverheadRate() {
var rent = parseFloat(document.querySelector('[name="MonthlyRent"]').value) || 0;
var utils = parseFloat(document.querySelector('[name="MonthlyUtilities"]').value) || 0;
var hours = parseInt(document.querySelector('[name="MonthlyBillableHours"]').value) || 1;
var rate = hours > 0 ? (rent + utils) / hours : 0;
document.getElementById('wzOverheadRate').value = '$' + rate.toFixed(2) + ' / hr';
}
document.querySelectorAll('.wz-overhead').forEach(function(el) {
el.addEventListener('input', updateWzOverheadRate);
});
updateWzOverheadRate();
// ── Labor Calculator ──────────────────────────────────────
function laborCalc() {
var employees = parseFloat(document.getElementById('lc_employees').value) || 0;