Skip to content

Product Offer

First-Pass Node Dossier

This page is an evidence-backed node dossier generated from the domain hierarchy and node questionnaire.

What It Is

ProductOffer is the main framework-owned pricing record for a product. It stores the sellable price inputs, targeting rules, and inventory-location context used to decide whether a shopper or order can buy a product at a given price.

Parent hierarchy:

  • Pricing & Promotions
  • Offer Resolution
  • Product Offer

Primary implementation paths:

  • packages/framework/src/Models/ProductOffer.php
  • packages/framework/src/Actions/ProductOffer
  • packages/framework/src/Http/Controllers/Api/Admin/ProductOffersController.php
  • packages/framework/src/Actions/Product/GetAvailableProductOffers.php
  • packages/storefront/src/Livewire/Storefront/Product/OfferBlock.php

What Users Can Do With It

Direct capabilities

Operators and integrations can:

  • create, update, list, and delete product offers through the admin API
  • export manufacturer-scoped offer data through Excel
  • synchronize offer data from ERP
  • inspect the currently available offers for a product in internal order-entry flows

Indirect capabilities

Other workflows consume product offers without exposing a packaged settings screen:

  • storefront product pricing resolves from available product offers
  • cart and order recalculation select the best eligible product offer
  • featured-offer calculation snapshots the best public or pro-deal offer by state and quantity
  • price history is logged when a product offer's current price changes

Where It Is Managed

ChannelRoleNotes
Admin APIDirectCRUD and list/filter operations live in ProductOffersController
Excel exportDirect read-onlyProduct offers can be exported by manufacturer
ERP bridgeDirectProduct offers sync from ERP DTOs
Admin order-entry dialogsDirect read-onlyOperators can inspect available offers while adding an item to an order
Storefront product pricingIndirectProduct and cart pricing resolve from available offers
Storefront offer APIDirect read-onlyDeprecated storefront API can still list offers for a product

Sources:

  • packages/framework/src/Http/Controllers/Api/Admin/ProductOffersController.php
  • packages/framework/src/Actions/ErpBridge/ProductOffer/SyncModelsFromErp.php
  • packages/storefront/src/Livewire/Storefront/Product/OfferBlock.php
  • packages/framework/src/Http/Controllers/Api/Storefront/ProductsController.php

Channel-Level Field Coverage

Admin create and update actions

The inspected product-offer create and update actions validate and accept:

  • product_inventory_item_id
  • product_inventory_location_id
  • customer_id
  • regular_price
  • sale_price
  • msrp_price
  • date_on_sale_from
  • date_on_sale_to
  • min_quantity
  • shipping_addresses
  • billing_addresses
  • states
  • countries
  • shipping_postal_codes

Sources:

  • packages/framework/src/Actions/ProductOffer/CreateProductOffer.php
  • packages/framework/src/Actions/ProductOffer/UpdateProductOffer.php

Admin API list shaping

The admin index request supports:

  • sorts by regular_price, sale_price, current_price, product_id, markup, margin_percentage, and product SKU
  • includes for product, taxonomy context, vendor, shipping-zone count, and price-history count
  • filters for product, custom-price, price-zone, SKU, manufacturer, category, type, inventory items, stock, customer eligibility, and margin/cost health

Sources:

  • packages/framework/src/Actions/ProductOffer/Requests/Admin/IndexRequest.php

Model fields that exist but were not confirmed in a packaged direct-edit UI

Important model-level fields include:

  • price_type
  • except_zips
  • target_price
  • price_rule_id
  • price_zone_id
  • custom_price_id
  • customer_group_ids
  • weighted_cost
  • custom
  • on_hold
  • currency
  • special_cost
  • extra_attributes

Source:

  • packages/framework/src/Models/ProductOffer.php

Configuration And Data Model

Key model characteristics:

  • soft-deletable
  • ERP-syncable
  • geographical
  • price-history aware
  • availability-aware through targeting traits

Important field groups:

Field GroupPurpose
product linkageproduct_id, product_inventory_item_id, product_inventory_location_id, product_vendor_id
pricing inputsregular_price, sale_price, msrp_price, current_price, target_price, weighted_cost, cost
targetingcustomer, customer groups, countries, states, postal codes, shipping and billing addresses
commercial stateprice_type, min_quantity, custom, on_hold

Calculation behavior:

  • Calculate runs latitude/longitude, current price, cost, markup, and margin-percentage recalculation
  • current price falls back to regular price unless a better sale price exists
  • current-price changes log a PriceHistory entry

Sources:

  • packages/framework/src/Models/ProductOffer.php
  • packages/framework/src/Actions/ProductOffer/Calculate.php
  • packages/framework/src/Actions/ProductOffer/CalculateCurrentPrice.php
  • packages/framework/src/Actions/ProductOffer/CalculateCost.php
  • packages/framework/src/Actions/ProductOffer/CalculateMarkup.php
  • packages/framework/src/Actions/ProductOffer/CalculateMarginPercentage.php

Relationships

ProductOffer directly relates to:

  • product
  • productInventoryItem
  • productInventoryItems
  • productInventoryLocation
  • productVendor
  • customer
  • customers
  • customerGroups
  • shippingPostalCodes
  • states
  • countries
  • shippingAddresses
  • billingAddresses
  • shippingZones
  • orderItems
  • priceHistories

Source:

  • packages/framework/src/Models/ProductOffer.php

Rules And Downstream Effects

Important rules and effects:

  • offer availability is filtered by customer, inventory location, shipping postal code, shipping address, billing address, and minimum quantity
  • stock-aware offer resolution prefers in-stock offers, then falls back to backorder-capable offers
  • storefront and cart best-offer resolution sorts by stock availability first, then by current_price, then by product_offers.id as a stable tie-breaker, and can further sort by closest inventory distance when inventory items are loaded
  • current-price changes create PriceHistory rows
  • offer creation recalculates the offer itself, but featured-offer recalculation is dispatched later from offer updates, deletes, restores, force deletes, and offer-inventory pivot changes because the initial create event fires before inventory items are attached

Sources:

  • packages/framework/src/Actions/Product/GetAvailableProductOffersQuery.php
  • packages/framework/src/Actions/Product/GetAvailableProductOffers.php
  • packages/framework/src/Actions/ProductOffer/CalculateCurrentPrice.php
  • packages/framework/src/Actions/Cart/Pipes/OffersAvailabilityCheck.php
  • packages/framework/src/Observers/ProductOfferObserver.php
  • packages/framework/src/Models/ProductOfferProductInventoryItem.php

Integrations And Automation

Excel

The packaged Excel surface provides a manufacturer-scoped product offers workbook for audit and review.

The worksheet columns are ordered as follows:

  1. Product ID
  2. SKU
  3. Product Name
  4. Product Type
  5. Collection
  6. Product Vendor
  7. Available
  8. MSRP
  9. Selling Price
  10. Measure Quantity
  11. Measure UOM
  12. Selling Price Per UOM
  13. Weighted Cost
  14. Avg Margin
  15. Avg Markup

Measure UOM includes the configured pricing base when it is greater than one, such as 100 ml. Selling Price Per UOM uses the product offer's existing per-measure price calculation and remains blank when the product's measurement metadata is incomplete. The three measurement columns are positioned after Selling Price; integrations that read the workbook by column position must account for the shifted cost, margin, and markup columns.

Source:

  • packages/framework/src/Actions/Exports/ProductOffer/ProductOfferTemplateExport.php
  • packages/framework/src/Actions/Exports/ProductOffer/Sheets/ProductOfferSheet.php

ERP bridge

Product offers have a real DTO-backed ERP sync flow.

Sources:

  • packages/framework/src/Actions/ErpBridge/ProductOffer/SyncModelsFromErp.php
  • packages/framework/src/Actions/ErpBridge/ProductOffer/UpdateOrCreateModelFromDTO.php
  • packages/framework/src/Services/ErpBridge/ProductOffer/ProductOfferErpSyncService.php
  • packages/erp-bridge/src/Requests/ProductOffers/GetProductOffersRequest.php

Storefront and order-entry consumers

Product offers are consumed by:

  • storefront product pricing blocks
  • the legacy storefront product-offers API
  • internal order-entry offer selection dialogs

Sources:

  • packages/storefront/src/Livewire/Storefront/Product/OfferBlock.php
  • packages/framework/src/Http/Controllers/Api/Storefront/ProductsController.php

Where It Appears To End Users

ProductOffer is not a first-class packaged settings page in the inspected admin UI, but it directly shapes what end users see.

It appears through:

  • storefront product pricing
  • product cards and PDP pricing wrappers
  • cart and order item pricing
  • internal order-entry offer selection

Sources:

  • packages/storefront/src/Livewire/Storefront/Product/OfferBlock.php
  • packages/storefront/src/Livewire/Storefront/Product/PricingWrapper.php
  • packages/storefront/src/View/Components/Product/Pricing.php

Current Documentation Takeaways

  • ProductOffer is the pricing center of gravity in the framework.
  • Offer targeting is broader than simple customer segmentation. Location, address, and inventory context also matter.
  • The inspected packaged product does not expose a first-class manual offer-management screen, so pricing ownership is split across API, ERP, export, and consumer flows.

Open Questions

  • This first pass did not identify a packaged admin CRUD screen for product offers comparable to the promotion settings pages.
  • price_rule_id, price_zone_id, and custom_price_id appear on the model and in admin filters, but their packaged ownership surfaces were not confirmed in this sprint.