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
@@ -5,7 +5,7 @@
ViewData["Title"] = $"Bill {Model.BillNumber}";
ViewData["PageIcon"] = "bi-receipt-cutoff";
ViewData["PageHelpTitle"] = "Bill Status";
ViewData["PageHelpContent"] = "Draft: editable, not yet confirmed. Open: awaiting payment. Partially Paid: some payments recorded. Paid: fully settled. Voided: cancelled preserves history. Edit is only available in Draft status. Use Void instead of deleting to keep a complete audit trail.";
ViewData["PageHelpContent"] = "Draft: editable, not yet confirmed. Open: awaiting payment. Partially Paid: some payments recorded. Paid: fully settled. Voided: cancelled &mdash; preserves history. Edit is only available in Draft status. Use Void instead of deleting to keep a complete audit trail.";
string StatusBadge(BillStatus s) => s switch
{
@@ -266,7 +266,7 @@
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="left" data-bs-trigger="focus"
data-bs-title="Balance Summary"
data-bs-content="Balance Due = Bill Total minus all payments recorded. Multiple partial payments are supported each reduces the balance until fully paid. Deleting a payment reverses it and restores the balance due.">
data-bs-content="Balance Due = Bill Total minus all payments recorded. Multiple partial payments are supported &mdash; each reduces the balance until fully paid. Deleting a payment reverses it and restores the balance due.">
<i class="bi bi-question-circle"></i>
</a>
</div>
@@ -322,7 +322,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Record Payment @Model.BillNumber</h5>
<h5 class="modal-title">Record Payment &mdash; @Model.BillNumber</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form asp-action="RecordPayment" method="post">
@@ -358,12 +358,12 @@
<a tabindex="0" class="help-icon" role="button"
data-bs-toggle="popover" data-bs-placement="left" data-bs-trigger="focus"
data-bs-title="Bank Account"
data-bs-content="The bank or cash account this payment is drawn from. Used for bank reconciliation helps match this payment to the corresponding debit on your bank statement.">
data-bs-content="The bank or cash account this payment is drawn from. Used for bank reconciliation &mdash; helps match this payment to the corresponding debit on your bank statement.">
<i class="bi bi-question-circle"></i>
</a>
</div>
<select name="BankAccountId" class="form-select" required>
<option value=""> Select Account </option>
<option value="">&mdash; Select Account &mdash;</option>
@foreach (var item in (IEnumerable<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem>)ViewBag.BankAccounts)
{
<option value="@item.Value">@item.Text</option>
@@ -423,7 +423,7 @@
<div class="col-12">
<label class="form-label fw-medium">Bank Account <span class="text-danger">*</span></label>
<select name="BankAccountId" id="editBillBankAccountId" class="form-select" required>
<option value=""> Select Account </option>
<option value="">&mdash; Select Account &mdash;</option>
@foreach (var item in (IEnumerable<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem>)ViewBag.BankAccounts)
{
<option value="@item.Value">@item.Text</option>