- Suppress ColorName line if it matches the item Name (powders use
color name as their item name, causing it to show twice)
- Suppress Finish if already contained in the item Name
- Always show Manufacturer regardless of whether it is populated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Subscription expiry (SubscriptionExpiryBackgroundService):
- Trials with no grace period now go directly Active -> Expired instead
of briefly entering GracePeriod for a day, which was causing repeated
'Grace Period Started' admin notification emails
- Remove redundant isTrial variable (query already filters to non-Stripe
companies, so all processed companies are trials by definition)
- Save per-company inside the loop so a single SaveChangesAsync failure
no longer discards all other companies' status changes and notification
log entries (which was the other cause of repeated emails)
HTML entities in page titles (33 views):
- Replace – / — with plain ' - ' in ViewData["Title"] C#
strings; Razor HTML-encodes these when rendering @ViewData["Title"],
causing browsers to display the literal text '–' instead of a dash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Vendor Create: reject duplicate company names (case-insensitive) before
saving; works for both the standalone form and the inline quick-add modal
- _Pagination: define changePageSize() JS function (was called but never
existed, breaking page size dropdown on every paginated list)
- Inventory Label: show bin/location on printed QR code labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an embed mode to the Label view (hides standalone nav controls) and
an iframe-based modal on Inventory Details. The modal footer Print button
calls contentWindow.print() so the print dialog opens without spawning a
new window.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
The HTML entity sweep script had a bug where it wrote empty files for any
view that contained no target Unicode characters, zeroing out 215 view files.
All views restored from the pre-sweep commit (cefdf3e).
Bulk gift certificate feature:
- BulkCreateGiftCertificateDto with Quantity (1-500), Amount, Reason, Expiry, Notes
- GenerateBulkGiftCertificatePdfAsync on IPdfService / PdfService: one Letter page
per cert, reusing the same purple/gold branded ComposeGiftCertificateContent helper
- GiftCertificatesController: BulkCreate GET/POST, BulkResult GET, BulkDownloadPdf POST
- Views: BulkCreate.cshtml (form with live total preview), BulkResult.cshtml (table +
Download All PDF button that POSTs cert IDs to avoid URL length limits)
- gift-certificate-bulk.js: live preview + spinner/disable on submit
- Index.cshtml: Bulk Create button added alongside New Certificate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sweeps em dashes, en dashes, multiplication signs, ellipses, and curly quotes
to their HTML entity equivalents (— – × … ‘ ’)
in all .cshtml files, skipping <script> blocks. Prevents encoding corruption
from AI tools and Windows encoding mismatches that caused recurring symbol bugs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>