mar-pod-b2b / module-b2b-shared-catalog
Company-specific catalog visibility and indexed catalog permissions for Marpod B2B Advanced.
Package info
gitlab.com/mar-pod-b2b/module-b2b-shared-catalog
Type:magento2-module
pkg:composer/mar-pod-b2b/module-b2b-shared-catalog
Requires
- php: ^8.5
- magento/framework: ^103.0
- magento/module-catalog: ^104.0
- magento/module-catalog-search: ^102.0
- magento/module-customer: ^103.0
- magento/module-graph-ql: ^100.4
- magento/module-indexer: ^100.4
- magento/module-store: ^101.1
- magento/module-webapi: ^100.4
- mar-pod-b2b/module-b2b-company: ^1.0
- mar-pod-b2b/module-b2b-core: ^1.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-08-24 16:10:44 UTC
README
Per-company visible assortment: which categories and products a company's storefront users can see at all, independent of price.
Status
Full catalog engine, indexer, cache integration, and Admin UI shipped. Assortment is assigned at
the company level (one company = one catalog); unit-level assignment (once
Marpod_B2bCompanyHierarchy is also installed) is a natural future extension but is not a hard
requirement today.
Implemented foundation
- declarative schema:
marpod_b2b_shared_catalog(public/custom catalogs, one public catalog per website created automatically),marpod_b2b_shared_catalog_categoryandmarpod_b2b_shared_catalog_product(category- and product-level assignment — assigning a category makes every product in it visible, on top of any individually-assigned product; the two mechanisms add together),marpod_b2b_shared_catalog_company(company-to-catalog assignment),marpod_b2b_shared_catalog_product_index(the flattened visibility index category listing pages query against via aJOIN, not by filtering a loaded collection); - a default public catalog is created automatically per website (via a
website_save_afterobserver plus a data patch for existing websites) so a company with no explicit assignment still sees the full standard catalog; custom (private) catalogs are assigned explicitly; SharedCatalogIndexBuilderInterface/ themarpod_b2b_shared_catalog_productindexer — full-rebuild per catalog on every run (assignment edits are rare in practice, so this trades a simpler implementation for not tracking partial/incremental changes);SharedCatalogVisibilityCheckerInterface— direct access to a product outside a company's catalog (e.g. typing the PDP URL directly) returns 404, not just a hidden listing row;- Full Page Cache correctness: product-listing blocks render as cacheable and do not see the
real customer session while a cache entry is being built (so the same HTML can be shared
safely across customers in the same group).
SetSharedCatalogHttpContextPluginresolves the customer'scatalog_idonce, early (ActionInterface::beforeExecute, the same hook level Magento's owncustomer_groupcontext plugin uses), stores it inMagento\Framework\App\Http\Context, and a separate plugin only reads it later duringLayer::prepareProductCollection— so Varnish/FPC correctly varies the cached page per catalog instead of serving one company's assortment to another.
Admin UI
Marpod_B2bSharedCatalog::manage (marpod_b2b_shared_catalog/catalog/index) provides a catalog
grid and a customer-style edit screen with General, Assigned Categories, Assigned Products,
and Assigned Companies tabs. Assignment grids are shown before separate assignment forms, page
actions use Magento's native action bar, and assignment POST actions restore the originating tab.
The screen also provides a manual reindex action.
APIs
Administrative REST operations require Marpod_B2bSharedCatalog::manage and are available below
/V1/marpod-b2b/shared-catalogs. They cover catalog CRUD plus category, product, and company
assignment reads and writes. Company assignment removal is available at
DELETE /V1/marpod-b2b/shared-catalog-companies/:companyId.
The storefront GraphQL query marpodB2bSharedCatalog returns the catalog resolved for the current
visitor and website, including assigned category IDs and directly assigned product IDs. A company
catalog is resolved from authenticated customer membership; guests and customers without an
explicit company assignment receive the website's default public catalog. A submitted company ID
is never trusted by the resolver.
Requirements
- PHP 8.5
- Magento 2.4.9-compatible framework packages
mar-pod-b2b/module-b2b-core,mar-pod-b2b/module-b2b-company
May be combined with Marpod_B2bPricing/Marpod_B2bAdvancedPricing, but does not require
either — catalog visibility and price are independent decisions in this design.
Installation
Run from the Magento project root:
composer require mar-pod-b2b/module-b2b-shared-catalog:^1.2
bin/magento module:enable Marpod_B2bSharedCatalog
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:clean
bin/magento indexer:reindex marpod_b2b_shared_catalog_product
Development
The planned scope, dependency rules, cache boundaries, security requirements, and test strategy
are documented in packages/B2B_LIGHT_PLAN.md in the development Magento project (section 9.2).
Unit tests:
vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist packages/module-b2b-shared-catalog/Test/Unit
REST/GraphQL API tests (real HTTP against a running instance — routing, authentication, ACL
enforcement, and cross-company/website 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_rest.xml \
packages/module-b2b-shared-catalog/Test/Api/Rest/SharedCatalogRestTest.php
vendor/bin/phpunit -c dev/tests/api-functional/phpunit_graphql.xml \
packages/module-b2b-shared-catalog/Test/Api/GraphQl/SharedCatalogGraphQlTest.php
Admin, REST, and GraphQL smoke test:
cd packages/module-b2b-shared-catalog/Test/Playwright
npm install
MAGENTO_BASE_URL=http://localhost:8090 \
MAGENTO_ADMIN_USER=playwright_test \
MAGENTO_ADMIN_PASSWORD='your-local-test-password' \
npm test
Limitations
- catalog search (OpenSearch) and the XML sitemap are not filtered by catalog assignment in this iteration — a product hidden from a company's category listing can still surface through full-text search or the sitemap. Treat this as a known gap if search-result leakage matters for your deployment;
- assignment is company-level only; per-hierarchy-unit assignment is not implemented.
Uninstallation
Do not remove the package while another installed module requires it. The
marpod_b2b_shared_catalog_* tables contain business data and are retained when the module is
disabled or its package is removed. Remove the marpod_b2b_shared_catalog_product indexer
schedule/state if disabling permanently.