6.04

Price Types & Calculation

Describe price types and generation: product page (base, special, catalog rules), cart (tiered, options, tax, cart rules), calculation process, customization, and price rendering.

Why This Matters: Understanding Magento's price layers is essential for accurate pricing logic. Product page prices use base price, special pricing, and catalog rules. Cart prices add tiered pricing, options, tax/VAT, and cart rules. Knowing how to customize calculation and rendering ensures correct pricing display.

Price Types & Calculation

mindmap root((Price Types)) Product Page Price Base price Special pricing Catalog rules Cart Price Tiered pricing Options price Tax and VAT Shopping cart rules Calculation Product Type Price calculatePrice Customization via plugins Rendering product price render default block Templates in product JS UI components

Price Generation Basics

Magento has multiple layers of pricing calculation depending on where the price is displayed.

Product Page Price (Before Cart)

  • Base Price: The price attribute value.
  • Special Pricing: Temporary discount set on product (date range optional).
  • Catalog Rules: Rule-based discounts from indexed catalogrule_product_price table.
Context: These three components determine the price shown on a product page.

Shopping Cart Price (After Quantity Determined)

Once a product is added to cart with a specific quantity, additional pricing applies:

  • Tiered Pricing: Volume discounts based on quantity, customer group, and website.
  • Options Price: Price added by custom options (e.g., engraving, gift wrap).
  • Tax / VAT: Applied depending on tax configuration and checkout stage.
  • Shopping Cart Rules: Promotional discounts applied in cart (e.g., "10% off cart total").
Key: Cart prices include quantity-dependent and cart-level discounts not visible on product page.

Price Calculation Process

The primary price calculation for product pages happens in the Price model.

\Magento\Catalog\Model\Product\Type\Price::calculatePrice()

Calculation Sequence (Product Page)

  1. Base Price: Start with price attribute or existing calculated price.
  2. Special Pricing: Apply if special_price is set and within date range.
  3. Catalog Rules: Apply lowest applicable rule price from catalogrule_product_price table.
// Simplified flow in calculatePrice()
$price = $product->getPrice(); // Base price

// Apply special price if applicable
if ($specialPrice && $specialPrice < $price) {
    $price = $specialPrice;
}

// Apply catalog rule price if lower
if ($rulePrice && $rulePrice < $price) {
    $price = $rulePrice;
}

return $price;

Identifying Final Price Components

To identify what composes a product's final price:

  1. Check Base Price: Product's price attribute.
  2. Check Special Price: special_price attribute and date range (special_from_date, special_to_date).
  3. Check Catalog Rules: Query catalogrule_product_price for active rules matching product/customer group.
  4. Check Tiered Pricing: Product's tier price table for quantity/customer group.
  5. Debug: Set breakpoint in calculatePrice() and step through.
Debugging Tip: Use Xdebug breakpoint in \Magento\Catalog\Model\Product\Type\Price::calculatePrice() to trace price calculation.

Customizing Price Calculation

Two approaches to customize price calculation:

  1. Plugins (Preferred): Use after plugin on calculatePrice().
  2. Preference: Replace entire price calculation class (more invasive).

Example: Plugin on calculatePrice()

di.xml:

<type name="Magento\Catalog\Model\Product\Type\Price">
    <plugin name="vendor_module_custom_price" 
            type="Vendor\Module\Plugin\Product\Type\Price" 
            sortOrder="10" />
</type>

Plugin Class:

namespace Vendor\Module\Plugin\Product\Type;

class Price
{
    public function afterCalculatePrice(
        \Magento\Catalog\Model\Product\Type\Price $subject,
        $result,
        $product,
        $qty = null
    ) {
        // Apply custom logic
        // Example: Add 10% markup
        return $result * 1.10;
    }
}
Best Practice: Use plugins to avoid overriding core logic entirely.

Price Rendering

Price rendering is configured via layout XML and uses dedicated renderer blocks.

Magento/Catalog/view/base/layout/default.xml

A block named product.price.render.default is created in default.xml.

Using Price Renderer Block

You can use this block to render pricing elsewhere in the application.

Example from Downloadable Product:

Magento/Downloadable/view/frontend/layout/catalog_product_view_type_downloadable.xml
<referenceBlock name="product.price.render.default">
    <arguments>
        <argument name="price_render" xsi:type="string">product.price.render.default</argument>
        <argument name="price_type_code" xsi:type="string">final_price</argument>
    </arguments>
</referenceBlock>

Price Rendering Templates

Templates for price renderers are located in:

Magento/Catalog/view/base/templates/product/

Key Templates

  • price/final_price.phtml
  • price/tier_prices.phtml
  • price/special_price.phtml
  • price/minimal_price.phtml
Customization: Override these templates in your theme to modify price display.

JavaScript UI Component for Prices

Magento also provides a JS UI component to render prices dynamically.

Magento/Catalog/view/base/web/template/

See: Render Prices on the Frontend

Use Cases

  • Dynamic price updates (e.g., when selecting configurable options)
  • AJAX-loaded prices
  • Custom price displays

How to Render Price in Custom Location

To render price in a custom location:

  1. In Layout XML:
    <block class="Magento\Framework\Pricing\Render" name="custom.price.render">
        <arguments>
            <argument name="price_render_handle" xsi:type="string">catalog_product_prices</argument>
        </arguments>
    </block>
  2. In Template (.phtml):
    <?php
    /** @var \Magento\Catalog\Block\Product\AbstractProduct $block */
    $product = $block->getProduct();
    ?>
    
    <?= $block->getLayout()
        ->getBlock('product.price.render.default')
        ->render(
            'final_price',
            $product,
            [
                'include_container' => true,
                'display_minimal_price' => true,
                'zone' => \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST
            ]
        ); ?>

Modifying Price Rendering

To modify how price is rendered:

  1. Override Templates: Copy template to your theme and modify.
  2. Custom Renderer: Create custom price renderer class extending \Magento\Framework\Pricing\Render\AbstractRenderer.
  3. Layout XML: Update catalog_product_prices.xml to use your renderer.
  4. CSS/JS: Style via CSS or add dynamic behavior with JS.
Example: Override final_price.phtml to add custom badges or formatting.

Price Types Summary

Price Type When Applied Description
Base Price Product Page Product's price attribute
Special Price Product Page Temporary discount with optional date range
Catalog Rules Product Page Rule-based discounts from indexed table
Tiered Pricing Cart Volume discounts based on quantity/group
Options Price Cart Custom option surcharges
Tax / VAT Cart/Checkout Tax based on configuration and location
Cart Rules Cart Promotional discounts in cart

Further Reading

Exam Tips

  • Product Page: Base price, special pricing, catalog rules.
  • Cart: Add tiered pricing, options price, tax/VAT, cart rules.
  • Calculation: Product\Type\Price::calculatePrice().
  • Customize: Plugin on afterCalculatePrice() or replace entire class.
  • Rendering: product.price.render.default block in default.xml.
  • Templates: Magento/Catalog/view/base/templates/product/.
  • JS UI: Magento/Catalog/view/base/web/template/ for dynamic prices.
  • Custom Render: Use layout XML to reference price renderer block.