Commit Graph

253 Commits

Author SHA1 Message Date
spouliot 92dc3ebd08 Add data access architecture spec and enforce rules in CLAUDE.md
Defines the target architecture for eliminating direct ApplicationDbContext
injection from controllers. Documents the three-tier model (generic repo,
typed domain repos, read services), the 6 typed repository interfaces to
build, the 2 reporting service interfaces to build, permanent exceptions,
and the 4-phase migration roadmap with per-controller checklist.

CLAUDE.md updated with the hard rule and tier quick-reference so every
session and every team member sees the constraint immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:35:16 -04:00
spouliot 5631d1d57a Add WarningPermanent toast type and upgrade invoice failure notifications
Email delivery failures and PDF generation errors now show a permanent
warning/error toast that requires manual dismissal, so users cannot
accidentally miss critical action-blocking feedback.

- ToastHelper: WarningPermanent TempData key + Warning/WarningPermanent
  extension methods on both ITempDataDictionary and Controller
- SetNotificationResultToast: NotificationStatus.Failed now uses
  ToastWarningPermanent (previously auto-dismissed in 5 s)
- InvoicesController.Send: TempData["Warning"] → TempData["WarningPermanent"]
  when PDF generation or email dispatch fails
- InvoicesController.DownloadPdf: TempData["Error"] → TempData["ErrorPermanent"]
  with the actual exception message so root cause is visible
- _Layout.cshtml: WarningPermanent hidden div
- toast-notifications.js: WarningPermanent handler (timeOut: 0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 16:02:16 -04:00
spouliot cad728ba66 Fix passkey login tracking, add email opt-out UI guards, and add Quick/Full quote mode toggle
- PasskeyController: set LastLoginDate on passkey sign-in so Company Health
  and audit pages show accurate last-login times (was always showing 'Never')
- Jobs/Index status modal: disable 'Notify customer' email toggle and show
  warning when customer has notifications turned off; CustomerNotifyByEmail
  added to JobListDto + JobProfile mapping + data-customer-notify attribute
- Quotes/Create: disable 'Send quote via email' checkbox with 'Notifications
  off' badge when selected customer has email opt-out; ViewBag.CustomerEmailOptOutIds
  added alongside existing CustomerTaxExemptIds pattern
- Quotes/Create: Quick Quote / Full Quote segmented toggle at top of form;
  hides non-essential fields (dates, notes, tags, oven, discount, photos) in
  Quick mode; selection persisted in localStorage
- InvoicesController Send action: improved error logging and user-facing
  warning when PDF generation or email dispatch fails after status is saved
- item-wizard.js: guard item restoration with try/catch; ensure writeHiddenFields
  always runs on form submit via capture-phase listener
- Help docs and AI knowledge base updated for all new features

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 13:32:34 -04:00
spouliot 0ea192d55b Harden legacy file paths and Twilio webhook validation 2026-04-26 18:14:16 -04:00
spouliot 8491b308eb Add admin email wizard and logging 2026-04-26 17:01:09 -04:00
spouliot 404ab3c45d Add unit tests for LedgerService, AccountBalanceService, DepositsController, and GiftCertificatesController
181 tests total passing. Covers all 9 LedgerService transaction sources, debit/credit
balance mechanics, AR/AP movements, date range filtering, running balance computation,
and future-dated opening balance exclusion. Also covers deposit recording/deletion,
gift certificate lifecycle (issue, void, lazy expiry), and account balance recalculation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 15:48:34 -04:00
spouliot 90a01571e3 Merge dev into master
- AI Catalog Price Check (Haiku model, rate limiting, progress bar, quarterly limit)
- Three-layer feature gating for AI Catalog Price Check (platform/plan/company)
- Passkey biometric login improvements (enrollment prompt, RPID fix, dismiss option)
- Company admin navigation consolidation (Subscription & Features button)
- Unit tests for 9 new services/controllers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 10:35:29 -04:00
spouliot a4b8ae611a Add passkey prompt dismissal and consolidate company admin navigation
- Add "Don't ask me again" to passkey enrollment prompt (PasskeyPromptDismissed
  field on ApplicationUser; DismissPrompt POST action; migration applied)
- Add Subscription & Features button to Companies/Index btn-group and
  Companies/Edit header for direct navigation to SubscriptionManagement/Manage
- Add Edit Company back-link on SubscriptionManagement/Manage
- Remove duplicate AI Features section from Companies/Edit (managed exclusively
  via Subscription & Features page)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 10:34:50 -04:00
spouliot 3899860c1f Fix PlatformSettings insert collision in AddAiCatalogPriceCheckGating migration
Replace InsertData (hardcoded ID 9) with raw IF NOT EXISTS SQL so the
migration is safe on environments where ID 9 is already taken.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 09:30:28 -04:00
spouliot f03a198e79 Make AiCatalogPriceCheckEnabled a plan-override toggle
Company-level toggle now grants access regardless of plan tier, checked
before the plan gate. Useful for enabling the feature on individual
Pro/Basic companies without upgrading their plan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 08:41:27 -04:00
spouliot cb7bbc37bd Add three-layer feature gating for AI Catalog Price Check
Adds platform-level, plan-level (Enterprise only), and per-company
toggles for the AI Catalog Price Check feature. Includes:
- Company.AiCatalogPriceCheckEnabled per-company flag
- SubscriptionPlanConfig.AllowAiCatalogPriceCheck plan-level flag
- PlatformSetting 'AiCatalogPriceCheckEnabled' global kill switch
- IPlatformSettingsService.GetBoolAsync helper
- ISubscriptionService.CanUseAiCatalogPriceCheckAsync
- UI controls in Companies/Edit, PlatformSubscription/Edit+Index,
  and SubscriptionManagement/Manage
- Migration AddAiCatalogPriceCheckGating applied

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 08:29:51 -04:00
spouliot fa9fa76231 Document AI Catalog Price Check in knowledge base and help article
- HelpKnowledgeBase: full AI price check section (verdicts, confidence,
  category paths, run limit, how to use, common questions)
- Inventory help article: new 'AI Catalog Price Check' section with
  verdicts, step-by-step instructions, caveats, and on-page nav link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 22:55:05 -04:00
spouliot 4128c15bbb Remove 'Claude' brand references from all user-facing views
Replace with generic 'AI', 'AI agent', or 'AI system' throughout.
Keeps the underlying vendor implementation details off the UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 22:51:36 -04:00
spouliot 6d9111b448 Rename button and add explanatory blurb to AI price check page
- Button: 'Run/Re-run Price Check' -> 'Analyze Catalog with AI'
- Add info card explaining what the analysis does, verdict meanings,
  and the disclaimer to verify operating costs before running

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 22:49:49 -04:00
spouliot 37c95192ca Enforce quarterly run limit on AI price check
- GET: sets ViewBag.NextRunAvailable if last run was within 90 days;
  view disables the button and shows the next eligible date
- POST: returns early with a warning if called before the 90-day window

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 22:48:39 -04:00
spouliot 03c10a3d77 Recalibrate progress bar to 27s/batch based on observed run time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 22:45:36 -04:00
spouliot ff79c39e83 Switch to sequential batching to eliminate rate limit hits
1 concurrent + 20s pacing = ~3 batches/min × 2k tokens = 6k TPM,
safely under the 8k output TPM limit. Progress estimate updated to 22s/batch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 22:27:10 -04:00
spouliot 2d25f6db2b Add proactive inter-batch pacing to avoid rate limit hits
Rather than relying on reactive 65s retries, each semaphore slot is held
for at least MinBatchIntervalSeconds (20s). With 2 concurrent slots that
limits throughput to ~3 batches/min × ~2k tokens = ~6k output TPM,
safely under the 8k/min limit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 22:01:22 -04:00
spouliot 47f186384f Increase progress bar estimate to account for rate-limit retry waits
25s per wave (was 10s) gives headroom for occasional 65s pauses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:55:17 -04:00
spouliot 26b8244422 Reduce to 2 concurrent batches to avoid Haiku output TPM bursting
3 concurrent batches hit the rate limit simultaneously then retry in
unison, causing repeated 429s. 2 concurrent keeps output rate lower.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:54:32 -04:00
spouliot 7b902d90a2 Restore 3 concurrent batches with Haiku; recalibrate progress bar
Haiku has generous rate limits so parallelism is safe again. Retry
logic catches any 429s. Progress estimate updated to ~8s per wave.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:49:53 -04:00
spouliot f05e16a826 Switch AI price check to Haiku for cost and speed
Testing Haiku 4.5 for catalog price analysis — structured JSON output
with explicit rules is well within its capabilities. Revert to Sonnet
if result quality is insufficient.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:41:50 -04:00
spouliot 97d47dbd1c Fix progress bar timing for sequential batch processing
Was still estimating based on 3 concurrent waves (old model).
Sequential mode runs ~18s per batch, so 500 items ≈ 6 minutes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:13:39 -04:00
spouliot 7407d1cd96 Fix rate limit errors in AI price check
Tier 1 Anthropic accounts are capped at 8,000 output tokens/minute on
Sonnet. 3 concurrent batches burst well past that, causing 429s.

- MaxConcurrentBatches: 3 → 1 (sequential prevents burst)
- Add retry: on rate_limit_error, wait 65s then retry up to 3 times
  so the per-minute window resets before the next attempt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:54:30 -04:00
spouliot 740238a939 Drop description field from AI price check user prompt
Item name + category path give Claude sufficient context for surface area
estimation. Descriptions add input tokens without meaningfully improving
verdict quality.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:44:25 -04:00
spouliot 560a2c76b8 Add full category path to AI price check for coating-type context
- Skip $0-priced items (placeholders/category headers) in RunAiPriceCheck
- Build full category path (e.g. "Cerakote > Firearms") via BuildCategoryPath
  so Claude receives coating-type context — Cerakote pricing differs significantly
  from standard powder coat
- Update AI system prompt to instruct Claude to use the category path when
  determining process type, equipment, cure times, and market rates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:35:41 -04:00
spouliot 19cc03ad1c Parallelize AI price check batches, increase batch size to 25
500-item catalog was making 50 sequential API calls, causing progressive rate-limit
throttling (explains "super slow towards the end") and ~$3 in credits.

- BatchSize: 10 → 25 (word limits are in place; 25 items × ~80 tokens ≈ 2000
  output tokens, well within MaxTokens=8192 — the original truncation cause)
- Run up to 3 batches concurrently via SemaphoreSlim(3) — independent API calls
  with no shared state, so no growing context issue
- For a 500-item catalog: 50 sequential calls → 20 calls in ~7 parallel waves,
  roughly 4× faster and 60% cheaper
- Dropped unused `costs` param from AnalyzeBatchAsync (system prompt has all costs)
- JS progress timing updated to reflect parallel waves

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:27:07 -04:00
spouliot 9370fcdd8f Reduce batch size to 10 and tighten AI price check prompt
Still seeing stubs despite MaxTokens=8192 — smaller batches and explicit
word limits in the prompt eliminate any remaining truncation risk.

- BatchSize: 15 → 10 (~1200 output tokens per batch vs. potential 3000+)
- Prompt: added 20-word cap on assumptions, 25-word cap on reasoning
- Prompt: strengthened "nothing before or after the '['" instruction
- Error log: now includes item IDs and first 300 chars of raw response
  so the next failure tells us exactly what Claude returned
- JS timing: updated batch divisor from 25 → 10 to match actual batch size

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 19:57:23 -04:00
spouliot 2c4c1a6846 Fix AI price check truncation and JSON parse errors
Root cause: MaxTokens=4096 was too low — 25 items at ~250 tokens each hit the
limit mid-array (logged error showed Path: $[17]).

- MaxTokens: 4096 → 8192
- BatchSize: 25 → 15 items (keeps each response well under the limit)
- StripJsonFences → ExtractJsonArray: now also handles prose before/after the
  JSON array, and recovers truncated responses by finding the last complete
  object and closing the array — so partial batches return whatever Claude
  finished rather than nothing
- GET action: added try-catch around ResultsJson deserialization so a bad DB
  row shows a friendly "re-run" warning instead of a raw error page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 19:45:53 -04:00
spouliot c9324ee0b0 Fix catalog-price-check.js served from wrong wwwroot
File was written to repo-root wwwroot/ instead of
src/PowderCoating.Web/wwwroot/ — causing a 404 and MIME type refusal.
Moved to the correct location.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 19:33:14 -04:00
spouliot 9943c11571 Add progress overlay to AI Catalog Price Check
Shows a modal overlay with animated progress bar and batch-aware status messages
while Claude is analyzing. Progress animates in two phases: ease-out to ~85%
over the estimated duration, then a slow crawl to 99% so it never falsely
"completes" before the server responds.

- Overlay driven by CSS (hidden until .active added by JS)
- Item count passed from controller as data-item-count on the run button
- Batch count derived from item count (batches of 25) to show accurate
  "Analyzing batch N of M…" messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 19:27:08 -04:00
spouliot 360edace72 Fix EnrollPrompt page layout squished on desktop
The auth panel CSS (brand panel gradient, form panel flex centering, feature list,
subtext color) was only defined in Login.cshtml's @section Styles — not in the
shared auth layout. EnrollPrompt used the same class names but had no styles behind
them, so the two-column layout collapsed. Added matching styles in EnrollPrompt's
own @section Styles block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 19:16:40 -04:00
spouliot 54f444d981 Add AI Catalog Price Check feature
Claude reviews every active catalog item against the shop's own operating costs
and returns a per-item verdict (below-cost / thin-margin / high / ok) with a
suggested price range, cost floor, and assumptions.

- New entity: CatalogPriceCheckReport (JSON blob, archived per company)
- New service: IAiCatalogPriceCheckService / AiCatalogPriceCheckService
  batches items 25 at a time to stay within model context limits
- Two new controller actions: GET AiPriceCheck (view report) + POST RunAiPriceCheck
- AiPriceCheck view: summary cards (counts by verdict), color-coded item cards
  with Edit Price link, assumptions detail, and loading spinner on submit
- AI Price Check button added to catalog Index header
- Migration AddCatalogPriceCheckReport applied

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:41:56 -04:00
spouliot dbe4170986 Add unit tests for 9 new services/controllers and expand existing test coverage
116 tests passing: JobPhotoService, MeasurementConversionService, PlatformSettingsService,
QuoteApprovalController, QuotePhotoService, ShopCapabilityCalculator, StorageMigrationService,
TenantContext, UsageQuotaController — plus expanded PricingCalculation, Registration, and
Subscription tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:27:30 -04:00
spouliot edce8e8c4a Move passkey enrollment prompt to post-login dedicated page
After password login, users are routed through /Passkey/EnrollPrompt
before reaching the dashboard. The page shows an Enable / Maybe later
choice using the auth layout for a clean full-screen experience.
Users who already have a passkey are skipped past instantly.

Removes the floating bottom-right card from _Layout — the dedicated
page is a better UX touchpoint (one moment, right after login, rather
than a floating card on every page).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 16:41:01 -04:00
spouliot 92f71f62d0 Fix iOS passkey enrollment sheet appearing on password form submit
Switch passkeySupported() from isConditionalMediationAvailable() to
isUserVerifyingPlatformAuthenticatorAvailable(). The conditional API
signals to iOS 17/18 that the page wants autofill passkey interception,
causing Safari to show its own native enrollment bottom sheet when the
password Sign In button is clicked. The platform authenticator check
simply asks if the device has biometrics, with no UI side-effects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:56:17 -04:00
spouliot c71332740e Fix passkey RPID mismatch across environments
Derive ServerDomain and Origin from the incoming HTTP request instead of
appsettings.json, so WebAuthn works on localhost, dev, and production
without any environment-specific configuration. Removed IFido2 from DI
and the Fido2 appsettings block — PasskeyController instantiates Fido2
per-request via BuildFido2().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:49:45 -04:00
spouliot edc599a1a2 Clean up TODO list and remove stale deploy_migration.sql
Completed items removed from TODO: AI catalog price check, catalog item
images, AI company lookup. deploy_migration.sql replaced by the
versioned scripts/042426_deploy_migration.sql.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:12:31 -04:00
spouliot 90a5a028ad Update docs and AI assistant for passkey biometric login
- HelpKnowledgeBase: passkey entry under USER PROFILE section with
  full how-it-works detail (setup, login flow, browser requirements,
  account-lock enforcement, per-device management)
- UserProfile help article: new Passkeys & Biometrics section between
  Two-Factor Auth and Appearance, with setup steps, login steps,
  browser compatibility note, and lost-device warning
- TOC nav link added to UserProfile article sidebar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:08:46 -04:00
spouliot 0bb96a502a Add passkey / biometric login (WebAuthn FIDO2)
Shop floor workers can log in once with a password, enroll a passkey,
and use Face ID / Windows Hello / fingerprint for all future logins.

- UserPasskey entity + AddUserPasskeys migration (Fido2 v4.0.1)
- PasskeyController: RegisterOptions, Register, LoginOptions, Login,
  Manage, Remove endpoints
- Login page: platform-aware button (Face ID / Windows Hello / etc.)
  hidden automatically if browser doesn't support WebAuthn
- Post-login floating prompt to enroll on first use; session-dismissed
- Passkeys & Biometrics link in user dropdown menu
- Manage page: list registered devices, add new, remove individual
- passkey.js: targeted base64url conversion (only challenge + user.id
  + credential IDs) — fixes "Required parameters missing" error caused
  by blindly converting rp.id and other string fields to ArrayBuffers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 15:07:01 -04:00
spouliot 4f976b1332 Require auth on all work order QR codes and add top view QR
- StatusBump (GET + POST) now requires authentication; routes by job ID
  instead of anonymous ShopAccessCode GUID; records actual user name in
  status history instead of anonymous token string
- WorkOrder action generates a second "View Job" QR in the header linking
  to the authenticated Details page (for verifying specs and seeing catalog
  images on mobile); status bump QR updated to ID-based URL
- WorkOrder view: top QR added to header alongside job number; status bump
  label updated (removed "no login required" copy)
- StatusBump view: updated form routing from asp-route-token to asp-route-id
- HelpKnowledgeBase and Jobs help article updated with two-tier QR docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 13:27:43 -04:00
spouliot 9361cd4495 Add production Jenkins pipeline for Azure App Service deployment
Fully manual pipeline (no triggers): build/test → publish → generate
idempotent EF migration SQL (archived as artifact) → apply to Azure SQL
via sqlcmd → ZIP deploy to App Service → smoke test.

Includes jenkins/Dockerfile (adds .NET 8 SDK, Azure CLI, mssql-tools18,
dotnet-ef 8.0.11 to jenkins/jenkins:lts) and .config/dotnet-tools.json
tool manifest.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 09:50:52 -04:00
spouliot 00bf8a4cd0 Add catalog item images with thumbnail preview in wizard
Each catalog item now supports one optional image (jpg/jpeg/png/gif/webp,
max 10 MB). Uploading generates a 200x200 JPEG thumbnail automatically via
SixLabors.ImageSharp. Images are stored in Azure Blob Storage under a new
catalogimages container, keyed by {companyId}/catalog/{itemId}/.

- CatalogItem entity: ImagePath + ThumbnailPath (nullable string fields)
- Migration: AddCatalogItemImages applied
- ICatalogImageService / CatalogImageService: upload, thumbnail generation,
  delete; old blobs replaced atomically on re-upload
- CatalogItemsController: Create/Edit accept optional IFormFile image;
  Image(id, thumbnail) action serves blobs with [Authorize] so wizard users
  can load thumbnails without CanManageProducts policy
- Catalog index (_CategoryNode): 40x40 thumbnail (or placeholder icon)
  left of each item name
- Details view: image card in right column with click-to-full-size link
- Create/Edit views: file picker with live preview; Edit shows current
  thumbnail with Remove checkbox
- Wizard (item-wizard.js): thumbnails in product list with hover preview
  that follows the cursor (showCatalogPreview / moveCatalogPreview);
  fixed Bootstrap d-flex !important bug that broke the filter box by
  moving flex layout to an inner wrapper div

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 09:33:59 -04:00
spouliot 931d6d40da Merge dev into master for deployment 2026-04-24 21:29:52 -04:00
spouliot 3327c86909 Add AI Profile draft generator and hide AI Quick Quote for release
- GenerateAiProfileDraft endpoint builds suggested AI Profile text from
  existing company config (ovens, workers, inventory categories, rates)
- "Generate from my settings" button wired in Company Settings AI Profile tab
- Add "hrs" unit label to Billable Hours/Month input in Company Settings and Setup Wizard Step 3
- Hide AI Quick Quote widget (commented out in _Layout) pending next release

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 21:29:42 -04:00
spouliot 27ac793f62 Harden paid registration flow and add unit tests 2026-04-24 21:10:28 -04:00
spouliot 4153acf3aa 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>
2026-04-24 19:35:00 -04:00
spouliot 813f76138c Fix web.config patch: use XPath instead of property navigation
Property-path navigation returns an empty string when the <location>
wrapper is absent from the published web.config, causing AppendChild
to fail. SelectSingleNode("//aspNetCore") works regardless of structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 17:25:50 -04:00
spouliot 8d94013895 Add AI Quick Quote widget and inline customer reassignment
- New AI Quick Quote floating button: staff type a verbal description to
  get an instant price estimate for phone/walk-in customers; detected
  color names are fuzzy-matched against inventory for stock status;
  saves draft quote under a Walk-In / Phone customer with one click
- Inline customer change on Quote Details and Job Details: always-visible
  native select with inline confirmation banner (no TomSelect dependency);
  ChangeCustomer AJAX endpoints on QuotesController and JobsController
- Quote Edit page: customer dropdown is now editable (lock removed)
- Fix AutoMapper missing CatalogCategory -> UpdateCategoryDto mapping
  that caused a crash on the catalog category Edit page
- Help docs and AI knowledge base updated for all three features

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 17:02:03 -04:00
spouliot fc9ddc6d17 Patch web.config with Development after each deploy 2026-04-24 10:04:41 -04:00