Pricing Storage & Display Reference
This page is the authoritative reference for how product pricing is stored and displayed across every layer of the Dealers Site. Pricing is now calculated in the SDM Middleware using nFusion data and imported into the dealer site.
Any pricing change must be reviewed across all dealer-facing and admin pricing surfaces:
- Trade page line items and cart totals
- Cart page line items and totals
POST /suma/v1/quotedraft API endpoint- WP Admin order edit view
- Order-update recalculation hooks and any downstream totals refresh logic
A change can look correct in one screen and still drift in another because these surfaces do not all read pricing from the same path.
Architecture Overview: Where Pricing Lives
nFusion API
│
▼
SDM Middleware (PriceSyncService)
- Fetches nFusion data (ask, tiers, spot, markup)
- Calculates final prices (spot + markup + adjustments)
- Generates volume tier breaks
│
├──▶ POST /suma/v1/prices ──────────────────────┐
│ │
▼ ▼
Dealer Site: WooCommerce Product Meta
_price, _regular_price, _markup_rate, _markup_mode, _metal_weight, _volume_pricing
│
├──▶ class-metal-pricing.php ←── reads meta (display & volume tier application)
│ │
│ ├──▶ Algolia index (price, markup_rate, markup_mode, retail_tiers — for search)
│ │
│ ├──▶ Cart REST API (unit_price, premium, tier info passed to Vue)
│ │
│ └──▶ Order line item meta (_unit_price, _premium, _metal_weight, _markup_mode)
│
└──▶ Vue widgets (apply volume tiers, apply catalog overrides, display pricing)
Spot Prices (Legacy — Reference Only)
Spot prices are no longer used for dealer site pricing calculations. The Middleware Platform calculates all prices and imports them pre-calculated. These options are retained for reference/display only.
Spot prices are pulled from the nFusion feed via:
herd php artisan nfusion:sync
They are stored as WordPress options and are never used for pricing calculations — they remain for reference/reporting:
| Option key | Description |
|---|---|
spot_price_gold | Gold spot price (USD per troy oz) — not used for pricing |
spot_price_silver | Silver spot price (USD per troy oz) — not used for pricing |
spot_price_platinum | Platinum spot price (USD per troy oz) — not used for pricing |
spot_price_palladium | Palladium spot price (USD per troy oz) — not used for pricing |
spot_price_copper | Copper spot price (USD per troy oz) — not used for pricing |
spot_price_modifier_gold | Historical modifier (not used) |
spot_price_modifier_silver | Historical modifier (not used) |
spot_price_modifier_platinum | (same pattern) |
spot_price_modifier_palladium | (same pattern) |
spot_price_modifier_copper | (same pattern) |
class-metal-pricing.php → get_spot_price( $metal ) reads these options (for reference), but they are not used in price calculations.
Product Meta Fields (Imported from Middleware)
All pricing data for a product is imported from the Middleware Platform and stored in WooCommerce product meta:
| Meta key | Type | Description |
|---|---|---|
_metal_type | string | gold, silver, platinum, palladium, copper. Empty = non-metal product. |
_metal_weight | float | Weight in troy ounces. Defaults to 1 if empty. |
_metal_weight_unit | string | Usually oz. |
_price | float | Final calculated ask price (imported from middleware) |
_regular_price | float | Final calculated price for WooCommerce (imported from middleware) |
_markup_rate | float | Markup value used in calculation (stored for reference/display). Meaning depends on _markup_mode. |
_markup_mode | string | Determines how _markup_rate was interpreted during middleware calculation. See Markup Modes below. |
_markup_rate_2 | float | Secondary markup rate (if applicable). |
_markup_sale_price | float | Optional override sale price. |
_volume_pricing | JSON | Array of tier breakpoints with calculated ask prices: [{"qty": 1, "ask": 28.50}, ...] (imported from middleware) |
These fields are imported via POST /suma/v1/prices from the Middleware Platform. Do not manually edit pricing meta fields on the dealer site — changes will be overwritten on the next sync.
Markup Modes (Reference — Calculated in Middleware)
The _markup_mode meta key reflects how the middleware calculated the price. It is stored for reference and used by frontend widgets to display pricing correctly.
| Mode | _markup_mode value | How Middleware Calculated | Frontend Display |
|---|---|---|---|
| Weight Fixed (default) | `` (empty) | Per-oz dollar premium | Premium shown per oz |
| Per Piece Fixed | each_fixed | Per-piece flat dollar premium | Premium shown per piece |
| Weight Percent | weight_percent | Percentage markup on spot | Percentage shown |
| Spot Only | spot | No premium — pure spot pricing | Spot price only |
| Fixed Price | (non-metal) | Direct fixed price | Fixed price displayed |
The unit price formula for each_fixed:
unit_price = (spot_per_oz × metal_weight) + markup_rate
So for the 10 oz bar at spot $75.524:
unit_price = (75.524 × 10) + 20.50 = $775.74
_markup_rate for each_fixed products is always stored as a per-piece total — even for multi-oz products. The per-oz conversion only happens in the display layer. Never change the stored value.
Per-Oz vs Per-Piece Premium Rule
This rule governs every display context — Vue badges, tier charts, cart totals, and admin columns.
| Product weight | Display rule |
|---|---|
>= 1 oz and each_fixed | Divide markup_rate by weight → show per-oz premium |
< 1 oz and each_fixed | Show markup_rate as-is → per-piece total |
| Any other mode | Show markup_rate as-is |
In code (Vue):
const weight = product.weight > 0 ? product.weight : 1;
const perOzMarkup = (markupMode === 'each_fixed' && weight >= 1)
? markup.value / weight
: markup.value;
In PHP (admin display):
$display_premium = ( $markup_mode === 'each_fixed' && $metal_weight >= 1.0 )
? $premium / $metal_weight
: $premium;
Algolia Index Attributes (Pricing)
When products are indexed by class-algolia.php, the following pricing attributes are written to each Algolia record. The Vue catalog widget reads these directly via InstantSearch / Algolia hits.
| Algolia attribute | Source | Notes |
|---|---|---|
weight | _metal_weight | Defaults to 1 if meta is empty |
weight_unit | _metal_weight_unit | Usually oz |
markup_rate | _markup_rate | Always the raw per-piece value for each_fixed |
markup_mode | _markup_mode | Used by Vue to select the display formula |
metal | _metal_type | gold, silver, etc. |
price | Calculated live unit price | Recalculated on each nFusion sync |
tiers | _volume_pricing | JSON tier array |
product.weight in the Vue widgets comes from this Algolia weight attribute. It is what drives the per-oz division in linePremium() and perOzTierMarkup().
Order Line Item Meta Fields
When a dealer adds a product to their cart and an order is created, these meta fields are snapshotted onto each order line item:
| Meta key | Type | What is stored |
|---|---|---|
_spot_price | float | Spot price per oz at the moment of cart add |
_premium | float | Premium value — see rule below |
_metal_weight | float | Product weight in oz at time of order |
_markup_mode | string | Markup mode at time of order |
_unit_price | float | Final calculated unit price at time of order |
_premium Storage Rule
_premium follows the same per-oz / per-piece rule as the display layer:
| Product weight | _premium stored as |
|---|---|
>= 1 oz and each_fixed | Per-oz premium (e.g. 2.05 for a 10 oz bar) |
< 1 oz and each_fixed | Per-piece premium total (e.g. 1.50 for a 0.5 oz coin) |
| Any other mode | Per-oz premium as calculated |
This is normalised in class-woocommerce-cart.php at reload/sync time, before the value is ever written to the order.
Order Recalculation Formula
When staff trigger a price recalculation from the WP Admin order edit screen (class-woocommerce-order-edit.php), the formula used is:
For each_fixed with weight >= 1oz (per-oz _premium):
unit_price = (spot_per_oz + _premium) × _metal_weight
Example: (75.524 + 2.05) × 10 = $775.74
For each_fixed with weight < 1oz (per-piece _premium):
unit_price = (spot_per_oz × _metal_weight) + _premium
For all other modes:
unit_price = (spot_per_oz + _premium) × _metal_weight
Admin Order Edit — Premium Column
The Premium column visible in WP Admin → Orders → Edit Order → line items reads _premium from the line item and displays it. Because _premium is now stored as per-oz for weight ≥ 1 oz products, no division is needed — the stored value is shown directly.
The column is rendered by class-woocommerce-order-edit.php → add_admin_order_item_values().
Vue Widget Premium Display
order-view — CatalogGridItem.vue
The .product-premium badge next to each product in the trade catalog:
// linePremium() in CatalogGridItem.vue
const weight = props.product.weight > 0 ? props.product.weight : 1;
return ( priceMode.value === 'Per Piece' && weight >= 1 )
? markup.value / weight
: markup.value;
priceMode is set by getPriceMode() in Store/index.js, which maps each_fixed → 'Per Piece'.
cart-view — CartItem.vue
The premium badge next to each line item in the cart:
// linePremium() in CartItem.vue
const weight = props.product.weight > 0 ? props.product.weight : 1;
return ( props.product.markup_mode === 'each_fixed' && weight >= 1 )
? markup.value / weight
: markup.value;
_shared — ItemDetails.vue (Tier Chart)
The expandable tier pricing table shown under each product:
// perOzTierMarkup() in ItemDetails.vue
const weight = props.product.weight > 0 ? props.product.weight : 1;
return ( props.priceMode === 'Per Piece' && weight >= 1 )
? tier.markup / weight
: tier.markup;
All three contexts apply the same per-oz / per-piece rule.
class-metal-pricing.php — Key Methods
| Method | Returns | Description |
|---|---|---|
get_spot_price( $metal ) | float | Current spot price from WP options (cached per request) |
get_price_data( $product ) | array | Full pricing array: metal_type, weight, markup_mode, markup_rate, markup, base_price, tiers, spot_price |
get_unit_price( $product, $qty ) | float | Live unit price for a given quantity (applies tier logic) |
calculate_markup( $spot, $rate, $modifier, $mode ) | float | Computes markup value from raw fields |
calc_spot_price_from_product( $metal, $weight, $unit_price ) | float | Back-calculates spot from the current product price |
Data Flow: Add to Cart → Order Line Item
1. Dealer clicks "Add to Cart" in the Vue catalog widget
└─ CatalogGridItem.vue → addCartItem() in Store/index.js
│
▼
2. POST to WooCommerce cart REST endpoint
└─ class-woocommerce-cart.php → add_to_cart()
│
▼
3. Cart reload (class-woocommerce-cart.php → reload())
└─ get_price_data() called for each cart item
└─ premium normalised to per-oz if each_fixed && weight >= 1
└─ _spot_price, _premium, _unit_price written to cart item session
│
▼
4. Order created at checkout
└─ Cart item meta copied to order line item meta
└─ _spot_price, _premium, _metal_weight, _markup_mode, _unit_price stored
│
▼
5. WP Admin → Edit Order
└─ Premium column reads _premium (already per-oz for weight >= 1)
└─ Recalculation uses (spot + _premium) × weight for each_fixed >= 1oz
Common Pitfalls
| Mistake | Correct approach |
|---|---|
Dividing _premium by weight in admin display | _premium is already per-oz for weight ≥ 1 — display it as-is |
Storing per-oz premium directly in _markup_rate for each_fixed | _markup_rate is always per-piece — only the display/storage normalisation layer converts |
| Assuming weight is always set | _metal_weight can be empty — always default to 1 |
| Hardcoding spot prices | Always read from spot_price_<metal> WordPress options |
Editing dist/app.js directly | Always edit source files and rebuild with npx mix --production |