Sweep all .cshtml files for encoding corruption; add pre-commit guard

Replace all corruption variants with HTML entities across 226 view files:
- 3-char UTF-8-as-Win1252 sequences (ae-corruption)
- Standalone smart/curly quotes that break C# Razor expressions
- Partially re-corrupted variants where the 3rd byte was normalised to ASCII

tools/Fix-Encoding.ps1: re-runnable sweep; uses [char] code points so the
script itself never contains a literal non-ASCII character; supports -DryRun

.githooks/pre-commit: blocks commits containing the ae-corruption byte
signature (xc3xa2xe2x82xac); git core.hooksPath = .githooks so the
hook is repo-committed and active for all future work on this machine.

Build clean; 225 unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 21:37:10 -04:00
parent 21b39161a3
commit a0bdd2b5b4
252 changed files with 1785 additions and 1633 deletions
+48 -48
View File
@@ -38,7 +38,7 @@
<i class="bi bi-lightbulb-fill flex-shrink-0 mt-1"></i>
<div>
The fastest way to create a job is to start with a quote. Build and approve the quote, then
click <strong>Convert to Job</strong> all items, coatings, and pricing carry over automatically.
click <strong>Convert to Job</strong> &mdash; all items, coatings, and pricing carry over automatically.
</div>
</div>
</section>
@@ -50,10 +50,10 @@
<p>To create a job manually:</p>
<ol class="mb-3">
<li class="mb-2">Go to <strong>Operations &rsaquo; Jobs</strong> and click the <strong>New Job</strong> button in the top-right corner.</li>
<li class="mb-2">Select the <strong>Customer</strong> this field is required. Type to search by name or company.</li>
<li class="mb-2">Enter a <strong>Job Description</strong> summarising the work (e.g., "Powder coat motorcycle frame gloss black").</li>
<li class="mb-2">Select the <strong>Customer</strong> &mdash; this field is required. Type to search by name or company.</li>
<li class="mb-2">Enter a <strong>Job Description</strong> summarising the work (e.g., "Powder coat motorcycle frame &mdash; gloss black").</li>
<li class="mb-2">Set the <strong>Scheduled Date</strong> (when work is expected to begin) and the <strong>Due Date</strong> (when the customer expects pickup or delivery).</li>
<li class="mb-2">Choose a <strong>Priority</strong> Normal is the default; see the Job Priority section below for all levels.</li>
<li class="mb-2">Choose a <strong>Priority</strong> &mdash; Normal is the default; see the Job Priority section below for all levels.</li>
<li class="mb-2">Optionally assign a <strong>Worker</strong> from your shop workers list.</li>
<li class="mb-2">Enter the customer's <strong>PO Number</strong> if they require one for their own records.</li>
<li class="mb-2">Add any <strong>Special Instructions</strong> your team needs to know before starting work.</li>
@@ -81,7 +81,7 @@
at any time by opening the job and editing it.
</p>
<p>
The two special statuses <strong>On Hold</strong> and <strong>Cancelled</strong> can be applied
The two special statuses &mdash; <strong>On Hold</strong> and <strong>Cancelled</strong> &mdash; can be applied
at any point in the workflow regardless of the current status. Use On Hold to pause a job temporarily
(waiting for customer approval, missing materials, etc.) and Cancelled to formally close a job that
will not be completed.
@@ -89,7 +89,7 @@
<p>
On the Jobs list, click any status badge to open a quick-change modal. The modal includes a
<strong>Notify customer via email</strong> toggle. If the customer has email notifications turned off,
that toggle is automatically disabled and a warning note is shown no email will be sent regardless.
that toggle is automatically disabled and a warning note is shown &mdash; no email will be sent regardless.
</p>
<div class="table-responsive">
@@ -176,7 +176,7 @@
</h2>
<p>
Every job has a priority level that tells your team how urgently a job needs to move through the shop.
Priorities are color-coded throughout the system in the job list, on the job details page, and on
Priorities are color-coded throughout the system &mdash; in the job list, on the job details page, and on
any dashboards. Set or change the priority in the Create or Edit form.
</p>
@@ -234,21 +234,21 @@
<h3 class="h6 fw-semibold mt-3 mb-2">Item Types</h3>
<ul class="mb-3">
<li class="mb-2">
<strong>Calculated Item</strong> enter surface area, quantity, and complexity. The system
<strong>Calculated Item</strong> &mdash; enter surface area, quantity, and complexity. The system
calculates material, labor, and equipment costs automatically. Select one or more powder
coatings and optional prep services (sandblasting, masking, cleaning).
</li>
<li class="mb-2">
<strong>Custom Work Item</strong> enter a free-text description and a manual price. Use this for
<strong>Custom Work Item</strong> &mdash; enter a free-text description and a manual price. Use this for
one-off work that does not fit the standard calculation model.
</li>
<li class="mb-2">
<strong>AI Photo Quote Item</strong> upload photos of the parts and let our AI agent estimate
<strong>AI Photo Quote Item</strong> &mdash; upload photos of the parts and let our AI agent estimate
the surface area, complexity, and labor time. Review and override any value before accepting.
Up to two follow-up rounds of questions are supported.
</li>
<li class="mb-2">
<strong>Labor Item</strong> a line item representing time and labor charges only, without
<strong>Labor Item</strong> &mdash; a line item representing time and labor charges only, without
material costs.
</li>
</ul>
@@ -268,8 +268,8 @@
<h3 class="h6 fw-semibold mt-3 mb-2">Prep Services</h3>
<p>
Each item can also include optional <strong>prep services</strong> sandblasting, masking, or
chemical cleaning that will be carried out before coating. These are selected in the wizard and
Each item can also include optional <strong>prep services</strong> &mdash; sandblasting, masking, or
chemical cleaning &mdash; that will be carried out before coating. These are selected in the wizard and
appear as sub-lines under the item on the job details page.
</p>
@@ -278,32 +278,32 @@
After completing the coatings and prep services steps, <strong>Calculated</strong> and
<strong>AI Photo Quote</strong> items include one final optional step: <strong>Save to Product Catalog</strong>.
This lets you turn the item you just configured into a reusable catalog entry so it can be selected
instantly on future quotes or jobs without re-entering dimensions, coatings, or prep services.
instantly on future quotes or jobs &mdash; without re-entering dimensions, coatings, or prep services.
</p>
<p>The wizard pre-fills the catalog form with:</p>
<ul class="mb-3">
<li><strong>Name</strong> taken from the item description (or AI-generated description); you can edit it</li>
<li><strong>Default Price</strong> copied from the item's calculated or manually adjusted unit price</li>
<li><strong>Description</strong> the item description</li>
<li><strong>Sandblasting / Masking</strong> automatically checked if those prep services were selected</li>
<li><strong>Category</strong> choose from your active catalog categories</li>
<li><strong>Name</strong> &mdash; taken from the item description (or AI-generated description); you can edit it</li>
<li><strong>Default Price</strong> &mdash; copied from the item's calculated or manually adjusted unit price</li>
<li><strong>Description</strong> &mdash; the item description</li>
<li><strong>Sandblasting / Masking</strong> &mdash; automatically checked if those prep services were selected</li>
<li><strong>Category</strong> &mdash; choose from your active catalog categories</li>
</ul>
<p>At the bottom of the step you have two options:</p>
<ul class="mb-3">
<li><strong>Save to Catalog &amp; Add</strong> saves the catalog item immediately and then adds the line item to the job.</li>
<li><strong>Skip Add to Job Only</strong> skips the save and adds the item to the job without creating a catalog entry.</li>
<li><strong>Save to Catalog &amp; Add</strong> &mdash; saves the catalog item immediately and then adds the line item to the job.</li>
<li><strong>Skip &mdash; Add to Job Only</strong> &mdash; skips the save and adds the item to the job without creating a catalog entry.</li>
</ul>
<div class="alert alert-permanent alert-info d-flex gap-2 mb-3" role="alert">
<i class="bi bi-info-circle-fill flex-shrink-0 mt-1"></i>
<div>
The catalog save happens immediately when you click <strong>Save to Catalog &amp; Add</strong> before the job form is submitted. The catalog item is preserved even if you later discard the job. You can view and manage all saved items at <a asp-controller="CatalogItems" asp-action="Index">Catalog Items</a>.
The catalog save happens immediately when you click <strong>Save to Catalog &amp; Add</strong> &mdash; before the job form is submitted. The catalog item is preserved even if you later discard the job. You can view and manage all saved items at <a asp-controller="CatalogItems" asp-action="Index">Catalog Items</a>.
</div>
</div>
<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>
Items on the job details page are grouped by type Catalog Items, Custom Work, and Labor to
Items on the job details page are grouped by type &mdash; Catalog Items, Custom Work, and Labor &mdash; to
make it easy to see exactly what work is being performed and which materials are required.
</div>
</div>
@@ -350,7 +350,7 @@
<i class="bi bi-receipt text-primary me-2"></i>Creating an Invoice from a Job
</h2>
<p>
Once a job is complete or at any time when you are ready to bill the customer you can create an
Once a job is complete &mdash; or at any time when you are ready to bill the customer &mdash; you can create an
invoice directly from the job's Details page. There is no need to manually re-enter pricing.
</p>
<ol class="mb-3">
@@ -428,7 +428,7 @@
<h3 class="h6 fw-semibold mt-3 mb-2">Job Notes</h3>
<p>
Add internal notes to a job from the Details page. Notes are private they are not visible
Add internal notes to a job from the Details page. Notes are private &mdash; they are not visible
to the customer. Use them for team communication, special handling instructions, or to log
anything notable that happened during production.
</p>
@@ -460,7 +460,7 @@
<i class="bi bi-clipboard2-check text-primary me-2"></i>Job Templates
</h2>
<p>
If you do the same type of work repeatedly for example, a standard wheel refinish package
If you do the same type of work repeatedly &mdash; for example, a standard wheel refinish package &mdash;
you can save a job's line items as a template and reuse it for future jobs.
</p>
<p>Templates are managed at <strong>/JobTemplates</strong>. To use a template:</p>
@@ -500,7 +500,7 @@
</h2>
<p>
The <strong>Part Intake</strong> workflow lets you formally check in a customer's parts at
drop-off before any work begins. This creates a timestamped record of how many pieces
drop-off &mdash; before any work begins. This creates a timestamped record of how many pieces
arrived, their condition, and who received them, which protects the shop if a customer later
disputes pre-existing damage or a missing piece.
</p>
@@ -513,10 +513,10 @@
On the intake form you will see:
<ul class="mt-1">
<li>A <strong>Job Summary</strong> card showing expected part count (from line items), due date, and any special instructions.</li>
<li><strong>Actual Part Count</strong> enter the number of pieces physically received. If this differs from the expected count, a warning appears prompting you to note the discrepancy.</li>
<li><strong>Condition Notes</strong> describe the condition of the parts (existing scratches, rust, missing hardware, special handling requirements, etc.).</li>
<li><strong>Advance to In Preparation</strong> toggle on to automatically move the job status to <em>In Preparation</em> at the same time. Leave off if the customer is dropping parts off early and work hasn't started yet.</li>
<li><strong>Before Photos</strong> upload photos documenting the condition at drop-off. Photos are saved as "Before" type on the job and appear in the Photos section of Job Details.</li>
<li><strong>Actual Part Count</strong> &mdash; enter the number of pieces physically received. If this differs from the expected count, a warning appears prompting you to note the discrepancy.</li>
<li><strong>Condition Notes</strong> &mdash; describe the condition of the parts (existing scratches, rust, missing hardware, special handling requirements, etc.).</li>
<li><strong>Advance to In Preparation</strong> &mdash; toggle on to automatically move the job status to <em>In Preparation</em> at the same time. Leave off if the customer is dropping parts off early and work hasn't started yet.</li>
<li><strong>Before Photos</strong> &mdash; upload photos documenting the condition at drop-off. Photos are saved as "Before" type on the job and appear in the Photos section of Job Details.</li>
</ul>
</li>
<li class="mb-2">Click <strong>Complete Intake</strong>.</li>
@@ -533,7 +533,7 @@
<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>
The intake form is optimised for use on a tablet at the front desk the layout is
The intake form is optimised for use on a tablet at the front desk &mdash; the layout is
touch-friendly and the photo upload works directly from a tablet camera.
</div>
</div>
@@ -547,7 +547,7 @@
<strong>Shop Mobile</strong> (<a href="/Jobs/ShopMobile">/Jobs/ShopMobile</a>) is a
phone and tablet-optimised view of all active jobs, designed for workers on the shop floor.
Unlike the Shop Display (which is a passive TV view) and the full desktop UI, Shop Mobile is
built for one-handed use large touch targets, no sidebar, and instant status advancement
built for one-handed use &mdash; large touch targets, no sidebar, and instant status advancement
with a single tap.
</p>
@@ -559,8 +559,8 @@
<li>Assigned worker, sandblasting/masking flags, and due date</li>
<li>Line items (up to 3 shown; tap the details button for the full list)</li>
<li>Powder colours</li>
<li>Special instructions (amber callout hard to miss)</li>
<li>An <strong>intake indicator</strong> a green box icon means parts were checked in; an amber box icon means intake is still pending (tap it to go directly to the intake form)</li>
<li>Special instructions (amber callout &mdash; hard to miss)</li>
<li>An <strong>intake indicator</strong> &mdash; a green box icon means parts were checked in; an amber box icon means intake is still pending (tap it to go directly to the intake form)</li>
<li>An <strong>overdue badge</strong> when the due date has passed</li>
</ul>
@@ -598,7 +598,7 @@
<i class="bi bi-person-gear text-primary me-2"></i>Changing the Customer
</h2>
<p>
The customer on a job can be changed at any time from the Job Details page no need to
The customer on a job can be changed at any time from the Job Details page &mdash; no need to
delete and re-create the job. This is useful when:
</p>
<ul class="mb-3">
@@ -610,16 +610,16 @@
<h3 class="h6 fw-semibold mt-3 mb-2">How to change the customer</h3>
<ol class="mb-3">
<li class="mb-2">Open the job from <strong>Operations &rsaquo; Jobs</strong> and go to its Details page.</li>
<li class="mb-2">Find the <strong>Customer</strong> field in the job header it appears as a dropdown showing the current customer.</li>
<li class="mb-2">Find the <strong>Customer</strong> field in the job header &mdash; it appears as a dropdown showing the current customer.</li>
<li class="mb-2">Select a different customer from the dropdown.</li>
<li class="mb-2">A confirmation banner appears: <em>"Change customer to [Name]?"</em> click <strong>Save</strong> to confirm or <strong>Cancel</strong> to revert.</li>
<li class="mb-2">A confirmation banner appears: <em>"Change customer to [Name]?"</em> &mdash; click <strong>Save</strong> to confirm or <strong>Cancel</strong> to revert.</li>
</ol>
<div class="alert alert-permanent alert-info d-flex gap-2 mb-0" role="alert">
<i class="bi bi-info-circle flex-shrink-0 mt-1"></i>
<div>
The customer can also be changed on the <strong>Edit Job</strong> page using the Customer
dropdown there. Any invoices or deposits already linked to the job are not automatically
moved update those separately if needed.
moved &mdash; update those separately if needed.
</div>
</div>
</section>
@@ -629,24 +629,24 @@
<i class="bi bi-qr-code text-primary me-2"></i>Work Order QR Codes
</h2>
<p>
Every printed job work order includes two tiers of QR codes one for <strong>viewing</strong>
Every printed job work order includes two tiers of QR codes &mdash; one for <strong>viewing</strong>
the job and a separate set for <strong>acting</strong> on it. This gives shop workers everything
they need from a printed sheet without touching the desktop app.
All QR codes require a logged-in account.
</p>
<h3 class="h6 fw-semibold mt-3 mb-2"><i class="bi bi-eye me-1"></i>Top QR View Job</h3>
<h3 class="h6 fw-semibold mt-3 mb-2"><i class="bi bi-eye me-1"></i>Top QR &mdash; View Job</h3>
<p>
Located in the work order header, next to the job number. Scan it with your phone to open the
full <strong>Job Details</strong> page items, catalog product images, powder specs, coatings,
full <strong>Job Details</strong> page &mdash; items, catalog product images, powder specs, coatings,
prep services, and special instructions. Use it to verify you're working the right job or to
see catalog item images on your phone without hunting through the app.
</p>
<h3 class="h6 fw-semibold mt-3 mb-2"><i class="bi bi-arrow-right-circle me-1"></i>Bottom QR Update Status</h3>
<h3 class="h6 fw-semibold mt-3 mb-2"><i class="bi bi-arrow-right-circle me-1"></i>Bottom QR &mdash; Update Status</h3>
<p>
Scan to open a mobile-friendly status bump page for this job. Tap the button to advance to the
next stage (or put the job on hold). The status change is recorded in history with your name
next stage (or put the job on hold). The status change is recorded in history with your name &mdash;
no anonymous bumps.
</p>
@@ -686,7 +686,7 @@
<h2 class="h5 fw-semibold mb-3">Blank Work Order</h2>
<p>
The Blank Work Order feature lets you instantly print a pre-formatted paper work order to hand to a
customer at drop-off before a digital job record has been created. It uses your company logo,
customer at drop-off &mdash; before a digital job record has been created. It uses your company logo,
address, and customizable terms so it looks professional right out of the box.
</p>
@@ -694,7 +694,7 @@
<ol>
<li>Go to <strong>Jobs</strong> in the sidebar.</li>
<li>Click the <strong><i class="bi bi-printer"></i> Blank Work Order</strong> button in the top-right toolbar (next to the Jobs Board button).</li>
<li>A PDF opens in a new browser tab print it or save it to PDF.</li>
<li>A PDF opens in a new browser tab &mdash; print it or save it to PDF.</li>
</ol>
<p>You can also navigate directly to <a href="/WorkOrder/Blank">/WorkOrder/Blank</a> to open the PDF at any time.</p>
@@ -714,8 +714,8 @@
Go to <strong>Company Settings → PDF Templates → Work Order</strong> to customize:
</p>
<ul>
<li><strong>Accent Color</strong> the color used for table headers, the title bar, and section labels. Defaults to dark gray.</li>
<li><strong>Terms &amp; Conditions</strong> up to 2,000 characters of text printed in italic below the notes box. Use this for your shop's standard policies, liability disclaimer, or payment terms.</li>
<li><strong>Accent Color</strong> &mdash; the color used for table headers, the title bar, and section labels. Defaults to dark gray.</li>
<li><strong>Terms &amp; Conditions</strong> &mdash; up to 2,000 characters of text printed in italic below the notes box. Use this for your shop's standard policies, liability disclaimer, or payment terms.</li>
</ul>
<p>Click <strong>Save</strong> to apply changes, or <strong>Preview</strong> to open the PDF instantly without saving.</p>