Initial commit

This commit is contained in:
2026-04-23 21:38:24 -04:00
commit 63e12a9636
1762 changed files with 1672620 additions and 0 deletions
@@ -0,0 +1,423 @@
@{
ViewData["Title"] = "Quotes";
}
<div class="d-flex align-items-center gap-2 mb-3">
<a asp-controller="Help" asp-action="Index" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i></a>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a asp-controller="Help" asp-action="Index">Help</a></li>
<li class="breadcrumb-item active">Quotes</li>
</ol>
</nav>
</div>
<div class="row g-4">
<div class="col-lg-9">
<section id="overview" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-info-circle text-primary me-2"></i>Overview
</h2>
<p>
Quotes let you provide customers and prospects with a formal price estimate before work begins.
The quoting engine calculates material costs, labor, equipment time, overhead, and profit margin
automatically based on the surface area and complexity you enter — no spreadsheet required.
</p>
<p>
Quotes can be created for existing customers or for <strong>prospects</strong> — people or
businesses who have not yet become customers. Prospect quotes let you capture all contact details
and pricing in one place so nothing is lost while you are waiting for a decision. If the prospect
accepts, you can convert them to a customer and the quote to a job in just a few clicks.
</p>
<p>
You can find Quotes under <strong>Operations &rsaquo; Quotes</strong> in the left sidebar.
The list is searchable by quote number or customer name, and filterable by status.
</p>
</section>
<section id="creating-a-quote" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-plus-circle text-primary me-2"></i>Creating a Quote
</h2>
<p>To create a new quote:</p>
<ol class="mb-3">
<li class="mb-2">Go to <strong>Operations &rsaquo; Quotes</strong> and click <strong>New Quote</strong>.</li>
<li class="mb-2">
Choose whether this quote is for an existing <strong>Customer</strong> or a <strong>Prospect</strong>:
<ul class="mt-1">
<li><strong>Customer</strong> — select from your existing customer list. The customer's pricing tier discount is applied automatically.</li>
<li><strong>Prospect</strong> — enter their first name, last name, company name (optional), email, and phone. These details are stored on the quote and can be used to create a customer record later.</li>
</ul>
</li>
<li class="mb-2">Set the <strong>Quote Date</strong> (defaults to today) and the <strong>Expiry Date</strong> (defaults to the system's configured validity period).</li>
<li class="mb-2">Add a <strong>Subject</strong> or description to identify the work being quoted.</li>
<li class="mb-2">Add one or more <strong>Line Items</strong> — see the Quote Items section below for item types.</li>
<li class="mb-2">Add any <strong>Notes</strong> for the customer (these appear on the printed quote).</li>
<li class="mb-2">Add any internal <strong>Notes</strong> that are for your team only.</li>
<li class="mb-2">Click <strong>Save Quote</strong>. The quote is saved as a Draft.</li>
</ol>
<p>
The system automatically generates a unique quote number in the format <code>QT-YYMM-####</code>
(for example, <code>QT-2503-0015</code>).
</p>
<div class="alert alert-permanent alert-info d-flex gap-2 mb-0" role="alert">
<i class="bi bi-lightbulb-fill flex-shrink-0 mt-1"></i>
<div>
You can save a quote as a Draft and come back to it later. Drafts are not visible to customers
and do not count as sent until you explicitly click <strong>Send Quote</strong>.
</div>
</div>
</section>
<section id="quote-items" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-list-check text-primary me-2"></i>Quote Items
</h2>
<p>
Each line item on a quote describes a distinct piece or group of parts to be coated. Items are
added using the item wizard, which walks you through selecting the type, entering details, and
choosing coatings and prep services. The total price updates in real time as you enter information.
</p>
<h3 class="h6 fw-semibold mt-3 mb-2">Item Types</h3>
<div class="row g-3 mb-3">
<div class="col-md-4">
<div class="card border-primary border-opacity-25 h-100">
<div class="card-header bg-primary bg-opacity-10 fw-semibold small">
<i class="bi bi-calculator me-1"></i> Calculated
</div>
<div class="card-body small">
Enter the surface area in square feet, quantity, and complexity. The system calculates
material cost, labor, and equipment time automatically. Choose one or more coatings
and optional prep services (sandblasting, masking, cleaning). Best for standard
powder coating work where you know the dimensions.
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-secondary border-opacity-25 h-100">
<div class="card-header bg-secondary bg-opacity-10 fw-semibold small">
<i class="bi bi-pencil me-1"></i> Custom Work
</div>
<div class="card-body small">
Enter a free-text description and type a price manually. Use this for one-off work,
repairs, or services that do not fit the standard surface-area calculation model.
No automatic pricing calculation — you set the price directly.
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-success border-opacity-25 h-100">
<div class="card-header bg-success bg-opacity-10 fw-semibold small">
<i class="bi bi-camera me-1"></i> AI Photo Quote
</div>
<div class="card-body small">
Upload photos of the parts and let the AI estimate the surface area and complexity.
The AI analyses the images and returns a suggested surface area (sq ft), complexity
rating, estimated minutes, and a confidence score. You can review and override any
value before accepting the estimate. Up to two follow-up rounds of questions are
supported.
</div>
</div>
</div>
</div>
<h3 class="h6 fw-semibold mt-3 mb-2">Coatings and Prep Services</h3>
<p>
For Calculated and AI Photo items, after entering the surface area you proceed to the coatings
step. Select one or more powder coatings from your inventory. The wizard shows how much powder
will be needed per coat based on the coverage rate and your surface area. You then choose any
prep services — sandblasting, masking, and/or cleaning — that will be performed before coating.
</p>
<h3 class="h6 fw-semibold mt-3 mb-2">Save to Product Catalog</h3>
<p>
After completing the prep services step, Calculated and AI Photo items display one final step:
<strong>Save to Product Catalog?</strong> This lets you add the item directly to your catalog
so it can be reused on future quotes and jobs without re-entering all the details.
</p>
<ul>
<li>The item <strong>Name</strong> is pre-filled from your description (or the AI-generated description for AI Photo items).</li>
<li>The <strong>Default Price</strong> is pre-filled with the accepted price for AI Photo items. For Calculated items, enter a fixed catalog price (the system-calculated price varies by dimensions, so a flat catalog price is set by you).</li>
<li>Choose a <strong>Category</strong> from your existing catalog categories.</li>
<li>Optionally add or edit a <strong>Description</strong> and flag whether the item typically requires sandblasting or masking.</li>
<li>Click <strong>Save to Catalog &amp; Add</strong> to save the item to the catalog and add it to the quote simultaneously.</li>
<li>Click <strong>Skip — Add to Quote Only</strong> to add the item to this quote without saving it to the catalog.</li>
</ul>
<div class="alert alert-permanent alert-warning d-flex gap-2 mb-3" role="alert">
<i class="bi bi-exclamation-triangle-fill flex-shrink-0 mt-1"></i>
<div>
<strong>Catalog item prices are final.</strong> When a catalog item is added to a quote or job,
the price you entered is used exactly as-is — no markup, no prep service charges, and no
complexity adjustments are added on top. Make sure the price you save already includes your
labor, materials, and margin.
</div>
</div>
<div class="alert alert-permanent alert-info d-flex gap-2 mb-0" role="alert">
<i class="bi bi-bookmark-star-fill flex-shrink-0 mt-1"></i>
<div>
The catalog save happens <strong>immediately</strong> — the item is added to your catalog even
if you later abandon the quote. This is intentional: once you take the time to describe and
price a part, it's worth keeping for next time. Manage saved items at
<a href="/CatalogItems">Catalog Items</a>.
</div>
</div>
<div class="alert alert-permanent alert-warning d-flex gap-2 mt-3 mb-0" role="alert">
<i class="bi bi-lightbulb-fill flex-shrink-0 mt-1"></i>
<div>
The live pricing calculator at the bottom of the quote form updates the subtotal, discounts,
tax, and grand total every time you add or change a line item. There is no need to manually
recalculate — the total shown when you save is the total the customer will see.
</div>
</div>
</section>
<section id="quote-statuses" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-tag text-primary me-2"></i>Quote Statuses
</h2>
<p>
Quotes move through a series of statuses that reflect where they are in the approval process.
Each status is shown as a color-coded badge on the quote list and details page.
</p>
<div class="table-responsive">
<table class="table table-sm table-bordered mb-0">
<thead class="table-light">
<tr>
<th style="width:30%">Status</th>
<th>What it means</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="badge bg-secondary">Draft</span></td>
<td>The quote is being prepared. It has not been sent to the customer and can be edited freely.</td>
</tr>
<tr>
<td><span class="badge bg-info text-dark">Sent</span></td>
<td>The quote has been delivered to the customer and is awaiting their response.</td>
</tr>
<tr>
<td><span class="badge bg-success">Approved</span></td>
<td>The customer has accepted the quote and authorised the work. Ready to convert to a job.</td>
</tr>
<tr>
<td><span class="badge bg-danger">Rejected</span></td>
<td>The customer has declined the quote. Useful to track win/loss rates over time.</td>
</tr>
<tr>
<td><span class="badge bg-warning text-dark">Expired</span></td>
<td>The quote's validity period has passed without a customer decision. Pricing may need to be revised before resubmitting.</td>
</tr>
<tr>
<td><span class="badge bg-primary">Converted</span></td>
<td>The quote was approved and converted into a job. The quote is now linked to the resulting job record.</td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="sending-a-quote" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-send text-primary me-2"></i>Sending a Quote
</h2>
<p>
Once a quote is saved as a Draft and you are happy with the pricing and details, you can mark it
as sent to the customer.
</p>
<ol class="mb-3">
<li class="mb-2">Open the quote from the Quotes list and go to its Details page.</li>
<li class="mb-2">Click <strong>Send Quote</strong>. The status changes from Draft to Sent.</li>
<li class="mb-2">If email notifications are configured for your company, the customer will automatically receive an email with the quote details.</li>
</ol>
<p>
You can also manually mark a quote as <strong>Approved</strong> or <strong>Rejected</strong> when
you hear back from the customer verbally or by phone, without going through a formal email send.
Use the status buttons on the quote Details page to do this.
</p>
<div class="alert alert-permanent alert-secondary d-flex gap-2 mb-0" role="alert">
<i class="bi bi-info-circle flex-shrink-0 mt-1"></i>
<div>
You can download a PDF of any quote to print or send via your own email client. Click the
<strong>Download PDF</strong> button on the quote Details page.
</div>
</div>
</section>
<section id="converting-to-job" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-arrow-right-circle text-primary me-2"></i>Converting to a Job
</h2>
<p>
When a quote is in the <strong>Approved</strong> status, you can convert it to a job in one click.
</p>
<ol class="mb-3">
<li class="mb-2">Open the approved quote and go to its Details page.</li>
<li class="mb-2">Click <strong>Convert to Job</strong>.</li>
<li class="mb-2">
A new job is created automatically, pre-filled with:
<ul class="mt-1">
<li>All line items from the quote (surface areas, quantities, coatings, prep services)</li>
<li>The final pricing calculated on the quote</li>
<li>The customer the quote was linked to</li>
</ul>
</li>
<li class="mb-2">Set the job's scheduled date, priority, and assigned worker, then save.</li>
</ol>
<p>
The quote status changes to <strong>Converted</strong> and a link to the new job appears on the
quote Details page. The job record also shows which quote it originated from.
</p>
</section>
<section id="prospect-conversion" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-person-check text-primary me-2"></i>Converting a Prospect to a Customer
</h2>
<p>
If a quote was created for a prospect (not yet in your customer list) and they decide to go ahead,
you can convert the prospect to a full customer record before converting the quote to a job.
</p>
<ol class="mb-3">
<li class="mb-2">Open the approved prospect quote.</li>
<li class="mb-2">Click <strong>Convert Prospect to Customer</strong>.</li>
<li class="mb-2">
The system opens a pre-filled customer creation form using the prospect's details from the
quote (name, company, email, phone). Review and complete any missing fields such as address,
customer type, or pricing tier.
</li>
<li class="mb-2">Save the new customer record.</li>
<li class="mb-2">The quote is now linked to the new customer. You can then click <strong>Convert to Job</strong> as normal.</li>
</ol>
<div class="alert alert-permanent alert-info d-flex gap-2 mb-0" role="alert">
<i class="bi bi-lightbulb-fill flex-shrink-0 mt-1"></i>
<div>
Only <strong>Approved</strong> prospect quotes show the Convert Prospect to Customer button.
If the quote is still in Draft or Sent status, approve it first before converting.
</div>
</div>
</section>
<section id="customer-approval-portal" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-person-check text-primary me-2"></i>Customer Approval Portal
</h2>
<p>
When you send a quote by email, the customer receives a link to a self-service approval portal
at <strong>/QuoteApproval</strong>. No login is required — the link is unique to that quote.
</p>
<p>From the approval portal the customer can:</p>
<ul class="mb-3">
<li class="mb-1">View the full quote with pricing breakdown.</li>
<li class="mb-1">Click <strong>Approve</strong> to accept the quote (status changes to Approved automatically).</li>
<li class="mb-1">Click <strong>Reject</strong> with an optional reason.</li>
<li class="mb-1">Pay a deposit online (if Stripe Connect is configured).</li>
</ul>
<p>
When a customer approves or rejects via the portal, you receive a notification and the quote
status updates in real time. You can then convert an approved quote to a job without needing
to contact the customer.
</p>
<p>
Approval links expire after the number of days configured in
<strong>Settings &rsaquo; App Settings &rsaquo; Quote Approval Token Days</strong> (default: 30 days).
</p>
</section>
<section id="deposits" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-piggy-bank text-primary me-2"></i>Deposits on Quotes
</h2>
<p>
You can record a deposit against an approved quote from the Quote Details page using the
<strong>Record Deposit</strong> button in the Deposits card. This is useful when a customer
pays a deposit before you start work and before a job or invoice has been created.
</p>
<p>
Deposits recorded on a quote carry over to the linked job automatically when you convert
the quote. They are then applied as payments when an invoice is created from that job.
</p>
</section>
<section id="pricing-breakdown" class="mb-5">
<h2 class="h4 fw-bold border-bottom pb-2 mb-3">
<i class="bi bi-bar-chart text-primary me-2"></i>Understanding the Pricing Breakdown
</h2>
<p>
On the Quote Details page, a full pricing breakdown section shows exactly how the grand total was
calculated. This transparency is useful when explaining pricing to customers and when reviewing
whether your rates are covering costs. The breakdown includes:
</p>
<ul class="mb-3">
<li class="mb-1"><strong>Material Costs</strong> — powder and consumables cost based on surface area and the configured cost-per-sq-ft rate.</li>
<li class="mb-1"><strong>Shop Supplies</strong> — a small percentage of material and labor costs covering miscellaneous shop consumables (tape, abrasives, etc.).</li>
<li class="mb-1"><strong>Labor Costs</strong> — base labor calculated from estimated job minutes. Sandblasting prep is charged at 1.5× the standard labor rate; masking at 0.5×. Additional coats beyond the first are charged at the configured additional coat labor percentage.</li>
<li class="mb-1"><strong>Equipment Costs</strong> — hourly rates for the curing oven, sandblaster, and coating booth, applied for the estimated time each piece of equipment is in use.</li>
<li class="mb-1"><strong>Complexity Adjustment</strong> — a percentage added based on the item's complexity rating (Simple, Moderate, Complex, or Extreme). Simple items have no adjustment; Extreme items can add 25% or more.</li>
<li class="mb-1"><strong>General Markup</strong> — your configured profit percentage applied to the cost subtotal. The exact formula depends on the <em>Pricing Mode</em> set in Company Settings: <em>Markup on Materials</em> adds the percentage on top of costs; <em>Target Margin on Total Cost</em> back-calculates price from a gross-margin target.</li>
<li class="mb-1"><strong>Rush Charge</strong> — applied automatically to jobs with Rush or Urgent priority, either as a percentage or a fixed amount depending on your settings.</li>
<li class="mb-1"><strong>Pricing Tier Discount</strong> — if the customer has a pricing tier assigned (e.g., Preferred Shop — 10% off), the discount is shown as a line item reduction. See <em>Hide Discount from Customer</em> below to control whether this line appears on the customer-facing PDF.</li>
<li class="mb-1"><strong>Tax</strong> — the configured tax rate applied to the final subtotal. Zero for tax-exempt customers.</li>
<li class="mb-1"><strong>Grand Total</strong> — the amount the customer will be invoiced.</li>
</ul>
<h3 class="h6 fw-semibold mt-4 mb-2">Per-Item Cost Breakdown</h3>
<p>
Each line item on the Quote Details page can be expanded to show a cost breakdown for that
individual item — click the row to open it. The per-item breakdown shows how material, labor,
equipment, complexity, and markup were calculated for that specific piece. This is useful for
spotting underpriced items or understanding where costs are concentrated across a multi-item quote.
</p>
<h3 class="h6 fw-semibold mt-4 mb-2">Hide Discount from Customer</h3>
<p>
When creating or editing a quote, you can check <strong>Hide Discount from Customer</strong>.
When this option is enabled:
</p>
<ul class="mb-3">
<li class="mb-1">The pricing tier discount line is <strong>not shown</strong> on the customer-facing quote PDF or on the online approval portal.</li>
<li class="mb-1">The discount is <strong>still applied</strong> to the total — the customer pays the discounted price, they just don&rsquo;t see the discount as a separate line item.</li>
<li class="mb-1">The full breakdown (including the discount) remains visible to your staff on the internal Quote Details page.</li>
</ul>
<p>
Use this when you want to honor a negotiated rate or volume discount without advertising
your tier structure to the customer.
</p>
<div class="alert alert-permanent alert-secondary d-flex gap-2 mb-0" role="alert">
<i class="bi bi-info-circle flex-shrink-0 mt-1"></i>
<div>
All rates used in the breakdown — labor, equipment, markup, shop supplies, complexity multipliers,
rush charge, and tax — are configured in <strong>Settings &rsaquo; Company Settings &rsaquo; Operating Costs</strong>.
See the <a asp-controller="Help" asp-action="Settings">Settings help page</a> for details on Pricing Mode and all rate settings.
</div>
</div>
</section>
</div>
<div class="col-lg-3 d-none d-lg-block">
@{ await Html.RenderPartialAsync("_HelpNav"); }
<div class="card border-0 shadow-sm sticky-top" style="top:80px">
<div class="card-header bg-transparent fw-semibold small text-muted text-uppercase" style="letter-spacing:.05em; font-size:.7rem;">On this page</div>
<div class="card-body p-0">
<nav class="nav flex-column">
<a class="nav-link py-1 px-3 small text-body" href="#overview">Overview</a>
<a class="nav-link py-1 px-3 small text-body" href="#creating-a-quote">Creating a Quote</a>
<a class="nav-link py-1 px-3 small text-body" href="#quote-items">Quote Items</a>
<a class="nav-link py-1 px-3 small text-body" href="#quote-statuses">Quote Statuses</a>
<a class="nav-link py-1 px-3 small text-body" href="#sending-a-quote">Sending a Quote</a>
<a class="nav-link py-1 px-3 small text-body" href="#converting-to-job">Converting to a Job</a>
<a class="nav-link py-1 px-3 small text-body" href="#prospect-conversion">Converting a Prospect</a>
<a class="nav-link py-1 px-3 small text-body" href="#customer-approval-portal">Approval Portal</a>
<a class="nav-link py-1 px-3 small text-body" href="#deposits">Deposits</a>
<a class="nav-link py-1 px-3 small text-body" href="#pricing-breakdown">Pricing Breakdown</a>
</nav>
</div>
</div>
</div>
</div>