Download the PHP package orangecat/module-prices without Composer
On this page you can find all versions of the php package orangecat/module-prices. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package module-prices
Orangecat_Prices
B2B pricing engine orchestrator — calculator pool, conflict resolution, and frontend price injection.
Module: Orangecat_Prices
Version: 1.0.0
License: OSL-3.0
Author: Oliverio Gombert [email protected]
Table of Contents
- Overview
- Theme Compatibility
- Requirements
- Installation
- What Gets Installed
- Configuration
- Store Admin Guide
- Buyer Guide (Frontend)
- Developer Guide
- REST API
- Frontend Routes Reference
- DevOps & Integrator Notes
Overview
Orangecat_Prices is the B2B pricing engine of the Orangecat suite. It does not store prices itself — it provides the infrastructure that downstream pricing modules (e.g., Orangecat_PricesList, Orangecat_PricesCompany) plug into to deliver company-specific prices to B2B buyers.
The module handles:
- A Calculator Pool that aggregates price calculators registered by downstream modules
- A Price Resolver that applies configurable conflict-resolution logic when multiple calculators return a price for the same product
- A Tier Price Resolver that aggregates quantity-based discount tiers across all calculators
- PHP-level price injection at three Magento hooks: product final price display, cart add, and quote-to-order conversion
- FPC/Varnish cache segmentation per company via HTTP context
- A frontend AJAX endpoint that resolves B2B prices for batches of products
- JavaScript widget mixins for Luma/RequireJS, Breeze, and Hyvä that update price displays without page reload
Position in the Orangecat B2B Dependency Chain
Orangecat_Prices depends on Orangecat_Company to resolve the company a logged-in customer belongs to. Orangecat_PricesList and Orangecat_PricesCompany register their calculators into this module's pool and provide the actual pricing data.
The Calculator Pool Pattern
This is the central architectural concept of the module. No prices are stored here. Downstream modules implement Orangecat\Prices\Api\PriceCalculatorInterface and register themselves in the CalculatorPool via di.xml. The PriceResolver calls each calculator for a given (SKU, qty, company) context and determines the winning price based on the configured Conflict Resolution Mode.
Two resolution modes are supported:
| Mode | Value | Behavior |
|---|---|---|
| Lowest Price | lowest_price |
The calculator returning the lowest price wins (best for buyer). Default. |
| Priority | priority |
The last calculator in di.xml sortOrder wins, regardless of value. |
The same resolution logic applies to quantity tiers in TierPriceResolver.
Theme Compatibility
| Theme | Status | Notes |
|---|---|---|
| Luma | Supported | RequireJS widget mixins (price-box-mixin, configurable-mixin, swatch-renderer-mixin, price-bundle-mixin) injected via requirejs-config.js. b2b-prices-core.js loaded as a global dependency on all pages. |
| Hyvä | Supported | Dedicated b2b-tier-prices_hyva.phtml template loaded on hyva_catalog_product_view. Vanilla JS using the native fetch() API — no jQuery dependency. Tier prices update on configurable variant selection via the configurable-selection-changed custom event. |
| Breeze Evolution | Supported | Breeze bundle defined in breeze_default.xml. breeze/prices-mixins.js uses $.mixinSuper() to patch configurable, SwatchRenderer, and priceBox Breeze components. Per-variant tier prices are fetched via the patched SwatchRenderer._OnClick and configurable._configureElement hooks. |
Requirements
| Dependency | Version / Notes |
|---|---|
| PHP | >= 8.1 |
magento/framework |
* |
magento/module-catalog |
* |
magento/module-configurable-product |
* |
magento/module-bundle |
* (bundle price resolution in AJAX endpoint) |
magento/module-customer |
* |
magento/module-quote |
* |
magento/module-sales |
* |
magento/module-store |
* |
magento/module-inventory-catalog-frontend-ui |
* (GetQty controller rewrite) |
orangecat/core |
* |
orangecat/module-company |
* |
Installation
Via Git Submodule (recommended for this project)
Enable the Module
Run inside the PHP container (reward shell):
Orangecat_Companymust be installed and enabled before this module.
What Gets Installed
Database Tables
None. Orangecat_Prices introduces no database tables or schema changes. All pricing data is owned by downstream modules (Orangecat_PricesList, Orangecat_PricesCompany).
EAV Attributes
None.
Data Patches
None. No default records, roles, CMS pages, or seed data are created by this module.
Configuration
Path: Admin menu → Prices → Settings, or Stores > Configuration > Orangecat > Prices (B2B)
Global B2B Pricing Engine
| Label | Config Path | Default | Description |
|---|---|---|---|
| Enable B2B Pricing Engine | prices/general/enabled |
Yes | Master switch. When disabled, all downstream pricing modules are bypassed and standard catalog prices are shown to all users. |
| Conflict Resolution Mode | prices/general/resolution_mode |
lowest_price |
When multiple calculators return a price for the same product, determines which wins. Options: Lowest Price (Best for Customer) or Priority (Last Module Executed). Hidden when the engine is disabled. |
| Use Company Tier Prices | prices/general/use_tier_prices |
Yes | Enables dynamic volume/tier pricing in AJAX responses. Disable to reduce payload and DB queries when tier pricing is not in use. Hidden when the engine is disabled. |
Config paths:
Store Admin Guide
Enabling / Disabling the B2B Pricing Engine
- Navigate to Admin menu → Prices → Settings (or
Stores > Configuration > Orangecat > Prices (B2B)). - Set Enable B2B Pricing Engine to
YesorNo. - Save configuration and flush cache.
When disabled, every B2B price override — flat prices and volume tiers — is suppressed for all users, including company members. All downstream pricing modules are silently ignored.
Choosing a Conflict Resolution Mode
This setting matters when more than one pricing module (e.g., Orangecat_PricesList plus a custom calculator) returns a price for the same product in the same company context:
- Lowest Price — always gives the buyer the best discount. Recommended default.
- Priority — the calculator with the highest
sortOrdervalue indi.xmlwins, regardless of which price is lower. Use when a specific pricing source must override all others unconditionally.
Tier Prices Toggle
When Use Company Tier Prices is enabled, the AJAX endpoint includes quantity tiers in its response and the frontend renders a "Buy X for $Y each" list on product detail pages. Disabling this setting reduces AJAX response size and database load when volume discounts are not configured.
Buyer Guide (Frontend)
B2B prices are applied automatically for any logged-in customer who belongs to a company. No buyer action is required.
- Product listing pages (PLP): A single batch AJAX request resolves prices for all products visible on the page. A brief loading state is applied to price boxes while the request is in flight.
- Product detail pages (PDP): The resolved B2B final price replaces the catalog price after page load. Volume tier prices ("Buy 5 for $9.00 each") appear below the main price block when configured. On configurable products, tier prices update automatically when the buyer selects a variant or swatch.
- Cart and checkout: The B2B price is enforced at the PHP level when items are added to the cart and when the quote is converted to an order. The correct company price is recorded on the order regardless of frontend state.
Logged-in customers who are not members of any company see standard catalog prices.
Developer Guide
Module Structure
Service Contract
PriceCalculatorInterface (Orangecat\Prices\Api\PriceCalculatorInterface)
Key Models
Config
PriceResolver
TierPriceResolver
Observers
| Class | Event | Area | Action |
|---|---|---|---|
ProcessFinalPriceObserver |
catalog_product_get_final_price |
frontend |
Calls PriceResolver::resolve() and calls $product->setFinalPrice(). Skips configurable, bundle, and grouped parent products (the engine is applied to their child simples instead). |
Plugins
| Class | Target | Hook | Purpose |
|---|---|---|---|
FinalPricePlugin |
Magento\Catalog\Pricing\Price\FinalPrice |
after getValue |
Overrides the displayed catalog price for simple / virtual / downloadable products with the B2B resolved price. |
FinalPricePlugin |
Magento\ConfigurableProduct\Pricing\Price\FinalPrice |
after getValue |
Same override for the configurable product type's final price model. |
CacheKeyPlugin |
Magento\Framework\Pricing\Render\PriceBox |
after getCacheKey |
Appends -{companyId}- to the block cache key so FPC/ESI cached price blocks are isolated per company. |
HttpContextPlugin |
Magento\Framework\App\ActionInterface |
before execute |
Injects orangecat_company_id into HTTP context for Varnish cache segmentation (called during action dispatch). |
HttpContextPlugin |
Magento\Framework\App\Http\Context |
before getVaryString |
Same injection for Magento's built-in FPC, which calls getVaryString before action dispatch during Kernel::load. Also injects customer_group and customer_logged_in context to fix a core FPC bug where load key and save key diverge. |
ToOrderItem |
Magento\Quote\Model\Quote\Item\ToOrderItem |
after convert |
Sets price, basePrice, originalPrice, and baseOriginalPrice on the new order item using the B2B resolved price. Skips configurable / bundle / grouped parent products. |
JS Components
Luma / RequireJS
| File | Purpose |
|---|---|
b2b-prices-core.js |
Collects all [data-product-sku] and [data-role="priceBox"] elements on page load, sends a single batch AJAX POST, and exposes window.b2bPricesPromise that all mixins await before patching. Also performs direct DOM patching for PLP simple products where the priceBox widget is never initialized. |
mixin/price-box-mixin.js |
Overrides _init; awaits b2bPricesPromise; patches options.prices, options.priceConfig.prices, and the display cache; calls reloadPrice(). |
mixin/configurable-mixin.js |
Patches spConfig.optionPrices with B2B variant prices and calls _reloadPrice() after a 50 ms guard to avoid race conditions with priceBox initialization. |
mixin/swatch-renderer-mixin.js |
Patches jsonConfig.optionPrices; calls _UpdatePrice(); overrides _OnClick to fetch per-variant tier prices on swatch selection. |
mixin/price-bundle-mixin.js |
Patches bundle selection prices in the bundle price widget. |
configurable-variation-qty-override.js |
RequireJS alias replacing Magento_InventoryConfigurableProductFrontendUi/js/configurable-variation-qty with a version that also resolves and renders B2B tier prices per selected variant. |
Breeze Evolution
| File | Purpose |
|---|---|
breeze/prices-mixins.js |
Single Breeze mixin file. Patches configurable._create and SwatchRenderer._create (via $.mixinSuper) to inject B2B variant prices into optionPrices. Overrides SwatchRenderer._OnClick and configurable._configureElement to fetch tier prices via inventory_catalog/product/getQty/ on variant change. Also patches priceBox._init for direct price box updates. |
Hyvä
| File | Purpose |
|---|---|
b2b-tier-prices_hyva.phtml |
Inline vanilla JS block rendered on hyva_catalog_product_view. Calls prices/ajax/ajaxprices on DOMContentLoaded to fetch tier prices for the main product. Listens for the configurable-selection-changed custom event to swap tier price display when the buyer selects a variant. |
Email Templates
None. This module sends no transactional emails.
ACL Resources
| Resource ID | Title | Location |
|---|---|---|
Orangecat_Prices::config |
B2B Prices | Stores > Settings > Configuration |
Adding Custom Logic
-
Register a new pricing source: Implement
PriceCalculatorInterface, then register the class inOrangecat_Prices'sCalculatorPoolvia your module'sdi.xml:The
sortOrdervalue controls resolution order whenresolution_modeis set topriority. - Override resolution mode per scope: The
prices/general/resolution_modeconfig supports website and store view scope. Override it inStores > Configurationto apply different conflict strategies per store. - Extend the AJAX response: Plugin
afterExecuteonOrangecat\Prices\Controller\Ajax\AjaxPricesto add extra keys to the JSON payload without modifying the core controller.
REST API
This module exposes no REST API endpoints. There is no webapi.xml. The AJAX pricing endpoint (prices/ajax/ajaxprices) is a standard frontend controller intended for storefront use only.
Frontend Routes Reference
| Route | Controller | Access |
|---|---|---|
POST /prices/ajax/ajaxprices |
Controller\Ajax\AjaxPrices |
Public — logged-in company members receive B2B prices; guests and non-company customers receive base catalog prices or an empty map. |
GET /inventory_catalog/product/getQty |
Controller\Rewrite\Product\GetQty |
Public — rewrites the core Magento controller to append tierPrices to the standard qty JSON response. |
AJAX Price Endpoint
URL: POST /prices/ajax/ajaxprices
Request body (JSON):
- All three arrays are optional. Pass
tier_product_idsonly on pages where tier price display is needed (PDP). - Maximum 200 entries per array.
Response (JSON):
configurable_pricesis only present when configurable products are in the request.tier_pricesis only present whentier_product_idsare passed andUse Company Tier Pricesis enabled.bundle_pricesis only present when bundle products are in the request.- Guests receive
{ "is_logged_in": false, "prices": {} }.
DevOps & Integrator Notes
Deployment Checklist
Integration Token Scope
This module exposes no REST API endpoints. No integration token ACL permissions are required for this module specifically.
FPC / Varnish Cache Segmentation
HttpContextPlugin injects orangecat_company_id into Magento's HTTP Vary context. Both Varnish and Magento's built-in FPC automatically create separate cache buckets per company ID — no custom Varnish VCL is required. The plugin also fixes a core Magento FPC bug where Kernel::load and Kernel::process produce different cache keys for logged-in customers, causing perpetual cache misses.
Disabling Without Uninstalling
Downstream modules that register calculators must be disabled first:
When disabled, all B2B price overrides are inactive. Standard catalog prices are shown to all users, including company members.
Data Integrity
This module introduces no database tables or schema changes. Disabling or uninstalling it leaves no residual data in the database. All pricing data is owned and managed by the downstream modules.
All versions of module-prices with dependencies
magento/framework Version *
orangecat/core Version *
orangecat/module-company Version *