Search by

mar-pod-b2b / module-b2b-dynamic-product-data

Company-context dynamic product data for Marpod B2B storefront and quote flows.

Maintainers

Package info

gitlab.com/mar-pod-b2b/module-b2b-dynamic-product-data

Issues

Type:magento2-module

pkg:composer/mar-pod-b2b/module-b2b-dynamic-product-data

Transparency log

Statistics

Installs: 1

Dependents: 2

Suggesters: 0

Stars: 0

1.0.9 2026-08-25 10:16 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-core and mar-pod-b2b/module-b2b-pricing for 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 LoggingDisabled, Basic, or Verbose; default: Disabled.
  • Log Retention Days — removes older database debug entries during the daily cron run; default: 30. Set 0 to 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-customer
  • POST /V1/marpod/b2b/dynamic-product-data/product-data/by-product-id/current-customer
  • POST /V1/marpod/b2b/dynamic-product-data/grouped-product-data/current-customer
  • POST /V1/marpod/b2b/dynamic-product-data/bundle-base-data/current-customer
  • POST /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