Quotes now reflect the current catalog price instead of a tenant's stale
typed-in cost, without disturbing accounting.
- InventoryItem gains CatalogReferencePrice + CatalogPriceUpdatedAt: the
QUOTING price (current replacement cost), kept separate from UnitCost/
AverageCost (the cost basis that drives valuation/COGS).
- The catalog sync (PowderCatalogUpsertService.PropagateToLinkedInventoryAsync,
run at the end of every upsert) refreshes linked inventory items with the
catalog's current price and product data (description, cure, SDS/TDS, color
families, coverage, SG, transfer eff, requires-clear-coat). It NEVER touches
cost, quantity, notes, image, location, or stock levels, and never nulls a
tenant value with a catalog null. EF persists only actual changes.
- CatalogReferencePrice is also set at link time (catalog receive, incoming-
from-catalog, identity match on create) so a freshly added powder quotes at
the current price immediately.
- Pricing now uses CatalogReferencePrice ?? UnitCost: the quote/job powder
pickers and PricingCalculationService (in-stock usage and powder-to-order
billing). Falls back to UnitCost for non-catalog/manual powders, so nothing
regresses. One current price for the whole quantity — no on-hand/to-order
split. Per-coat snapshot still locks the price at quote creation.
Tests: propagation updates reference price + specs but not cost/qty/notes/
image, and skips a $0 catalog price. Full suite 276 green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>