mar-pod-b2b / module-b2b-dynamic-product-data
Company-context dynamic product data for Marpod B2B storefront and quote flows.
Package info
gitlab.com/mar-pod-b2b/module-b2b-dynamic-product-data
Type:magento2-module
pkg:composer/mar-pod-b2b/module-b2b-dynamic-product-data
Requires
- php: ^8.5
- magento/framework: ^103.0
- magento/module-bundle: ^101.0
- magento/module-catalog: ^104.0
- magento/module-customer: ^103.0
- magento/module-graph-ql: ^100.4
- magento/module-grouped-product: ^100.4
- magento/module-inventory-api: ^1.2
- magento/module-quote: ^101.2
- magento/module-search: ^101.1
- magento/module-webapi: ^100.4
- mar-pod-b2b/module-b2b-company: ^1.0
- mar-pod-b2b/module-b2b-core: ^1.0
- mar-pod-b2b/module-b2b-pricing: ^1.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
Replaces
None
This package is auto-updated.
Last update: 2026-08-25 08:17:02 UTC
README
Magento 2 module for company-context product pricing, availability, delivery data, and quote validation.
The module keeps public HTML customer-neutral. Company-specific values are loaded after the page through Magento customer-data and private REST responses, preserving compatibility with Full Page Cache and Varnish.
Requirements
- PHP 8.5
- Magento Open Source or Adobe Commerce 2.4.9
- Magento Bundle, Catalog, Customer, Grouped Product, Inventory API, Quote, Search, and Web API modules
mar-pod-b2b/module-b2b-coreandmar-pod-b2b/module-b2b-pricingfor company-price resolution
The package does not require MageSuite or Smile ElasticSuite.
Installation
After publishing the repository on Packagist, install the package from the Magento root:
composer require mar-pod-b2b/module-b2b-dynamic-product-data:^1.0
bin/magento module:enable Marpod_B2bDynamicProductData
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:clean
The module is disabled by default. Enable it at Stores > Configuration > Marpod > Marpod Configuration > Dynamic Catalog.
Configuration
Configuration supports default, website, and store-view scope:
- Enabled — activates storefront and quote integrations; default:
No. - Debug Logging —
Disabled,Basic, orVerbose; default:Disabled. - Log Retention Days — removes older database debug entries during the daily cron run; default:
30. Set0to retain entries until manual cleanup. - Clear Dynamic Cache — invalidates only customer-specific data generated by this module.
Debug entries are stored in marpod_b2b_dynamic_product_data_debug_log. File logging uses var/log/marpod_b2b_dynamic_product_data.log.
The cache can also be cleared from the command line:
bin/magento marpod:b2b:dynamic-product-data:cache:clear --all
Supported storefront behavior
- product pages and native category/search listings;
- simple, configurable, grouped, and bundle products;
- company-specific prices, availability, and delivery data;
- dynamic quote prices and quantity validation;
- targeted invalidation after relevant catalog or inventory changes;
- admin debug log listing and scheduled cleanup.
Connect a production data source
The included Model/TestProductDataProvider reads development data from marpod_b2b_dynamic_product_data_customer_price_test and falls back to Magento product data. A production integration should implement:
\Marpod\B2bDynamicProductData\Api\ProductDataProviderInterface
Configure the implementation in the consuming module's etc/di.xml:
<preference for="Marpod\B2bDynamicProductData\Api\ProductDataProviderInterface"
type="Vendor\Module\Model\ErpProductDataProvider"/>
Implementations may use an ERP, PIM, remote API, or project-owned database. They must return ProductContextInterface values for SKU/company and product-ID/company lookups. TestProductDataProviderInterface remains available as a deprecated compatibility alias.
Company pricing
Model\CompanyPriceOverlayApplier resolves the current pricing context via
Marpod\B2bCore\Api\Pricing\PricingContextResolverInterface (provided by mar-pod-b2b/module-b2b-pricing)
and, only when the visitor is browsing in company scope, overlays the company's price from
Marpod\B2bPricing\Api\CompanyPriceResolverInterface on top of whatever ProductDataProviderInterface
already resolved. Guests and individually-priced customers are unaffected. A company with no active
price list, or no price for a given SKU, keeps the base provider's price unchanged — a missing company
price never hides a product.
The overlay is applied in three places: CurrentCustomerProductDataProvider (storefront listing/search,
bulk), CurrentCustomerProductDataByIdProvider (product page, single SKU), and
QuoteItemDynamicPriceApplier (cart/checkout price application, including bundle child and
bundle-with-own-price items).
Development fixture
Test/Fixture/customer_product_prices.sql creates customer-specific development prices for existing customer/product pairs. It is never loaded automatically. See Test/Fixture/README.md before use.
Example for a Docker project when this repository is installed as a Composer path package:
docker compose exec -T db mariadb -uroot -proot magento \
< packages/module-b2b-dynamic-product-data/Test/Fixture/customer_product_prices.sql
REST endpoints
POST /V1/marpod/b2b/dynamic-product-data/product-data/current-customerPOST /V1/marpod/b2b/dynamic-product-data/product-data/by-product-id/current-customerPOST /V1/marpod/b2b/dynamic-product-data/grouped-product-data/current-customerPOST /V1/marpod/b2b/dynamic-product-data/bundle-base-data/current-customerPOST /V1/marpod/b2b/dynamic-product-data/bundle-product-data/current-customer
Responses contain private customer data and must not be stored in shared caches.
GraphQL
marpodB2bProductData(sku), marpodB2bGroupedProductData(sku), and
marpodB2bBundleProductData(sku) are the headless/PWA equivalent of the REST routes above,
resolving the customer ID explicitly from the GraphQL request context rather than PHP session (a
stateless, token-authenticated GraphQL request doesn't populate the session the same way REST's
"current-customer" routes rely on) — 0 means guest, matching the REST routes' own anonymous
behavior.
marpodB2bProductData additionally applies the company price-list overlay itself, resolving
company context from the GraphQL request (via Marpod\B2bCompany\Api\CompanyMembershipInterface)
and calling Marpod\B2bPricing\Api\CompanyPriceResolverInterface::resolveMultiple() directly,
rather than reusing CompanyPriceOverlayApplier — that class's company-context resolution
(Marpod\B2bPricing\Model\PricingContextResolver) is session-based and would not reliably resolve
company context for this transport. Grouped and bundle product data are exposed as-is; no
equivalent company-price overlay exists for those types anywhere in this codebase today.
Tests
The repository contains unit, Magento integration, and Playwright smoke tests:
vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist \
vendor/mar-pod-b2b/module-b2b-dynamic-product-data/Test/Unit
For a path repository, replace the test path with packages/module-b2b-dynamic-product-data/Test/Unit.
REST/GraphQL API tests (real HTTP against a running instance — routing, authentication, and
company-price isolation; see mar-pod-b2b/module-b2b-core's README for the shared Test/Api base
classes these build on):
vendor/bin/phpunit -c dev/tests/api-functional/phpunit_graphql.xml \
packages/module-b2b-dynamic-product-data/Test/Api/GraphQl/ProductDataGraphQlTest.php