Le guide ultime des permissions de catalogue de Magento 2 pour les boutiques B2B et B2C
The Ultimate Guide to Magento 2's Catalog Permissions for B2B and B2C Stores
Hey — if you’re building or maintaining a Magento 2 store and you’ve been asked to lock down parts of your catalog for certain clients, or to show different things to wholesalers versus retail shoppers, this guide is for you. I’ll keep it relaxed, practical, and show étape-by-étape code exemples so you can try stuff quickly on a dev environment.
This post covers the fundamentals you need to know about catalog permissions in Magento 2, how B2B and B2C prérequis differ, comment configure permissions for out-of-stock products, integnote stock-management modules (like Force Product Stock Status) for consistent visibility, and advanced cas d'utilisation like private catalogs, negotiated prixs, and geographic restrictions. I’ll include real config snippets, sample observateurs/plugins, and CLI commands — nothing vague.
Why catalog permissions matter
Catalog permissions let you control which categories and products are visible to which clients or groupe de clientss. For marketplaces and multi-channel sellers, they prevent prix leakage, enable private B2B catalogs, and can hide discontinued or restricted products. In Adobe Commerce (Magento Commerce) there is a built-in Catalog Permissions fonctionnalité. For Magento Open Source, you can replicate similar behavior with custom modules or tiers extensions.
Quick glossary
- Customer Group — groups like General, Wholesale, Retailer, or custom B2B groups.
- Catalog Permissions — rules that limit visibility at category/product level.
- Visibility — whether a product is visible in catalog/recherche or both.
- Stock status — in stock/out of stock, and sometimes forced by extensions.
Fundamental differences: B2B vs B2C catalog permission needs
Avant jumping into configs, it’s crucial to understand why B2B and B2C require different approchees.
B2C typical needs
- Public catalog for all visiteurs.
- Basic personalization (related products, targeted promotions).
- Out-of-stock products often shown with call-to-action to subscribe or backcommande.
- Price visibility is generally the same for everyone (unless you do segmentation).
B2B typical needs
- Private catalogs for logged-in company utilisateurs.
- Different product sets per company or groupe de clients.
- Negotiated prixs and tiered tarification (often company-specific).
- Strict control of who can see which SKUs (preventing competitors from seeing products/prixs).
- Special handling of out-of-stock items (maybe hide entirely or show availability to sales reps only).
So, the rules: for B2C you usually show more, for B2B you usually hide more and apply more granularity.
How Catalog Permissions work in Magento 2 (high-level)
In Adobe Commerce, Catalog Permissions is a module that adds permission entities and admin UI to assign permissions to categories/products per groupe de clients and store. It can affect:
- Category view and product view visibility
- Price visibility
- Search visibility
At runtime, Magento checks these permission rules before rendering category and page produits. If a product is restricted for a groupe de clients, the product is excluded from lists, and direct access peut être blocked or redirected.
Configuring catalog permissions: practical étapes
Here’s a practical path:
- Plan: map groupe de clientss and catalog segments (categories/products) to permission rules.
- Enable Catalog Permissions (if you’re on Adobe Commerce) or install a tested module for Open Source.
- Create permission rules in Admin or by code.
- Adjust stock/visibility interplay (see next section).
- Test thoroughly: logged-out utilisateur, different groupe de clientss, direct product URL access, fulltext recherche results, and API responses.
Admin UI (Adobe Commerce)
Go to Stores > Configuration > Catalog > Catalog Permissions to enable and set defaults. To create rules: Marketing > Catalog Permissions > Add New Permission (or a similar path depending on version).
Creating a simple permission rule by code
Si vous prefer to déployer permissions via code (useful in CI/CD), you can add a script de setup or data correctif. Example (simplified) data correctif that creates a category permission for a groupe de clients:
moduleDataSetup = $moduleDataSetup;
$this->permissionFactory = $permissionFactory;
}
public function apply()
{
$data = [
'customer_group_id' => 3, // wholesale
'category_id' => 42,
'website_id' => 1,
'grant_catalog_category_view' => 1,
'grant_catalog_product_price' => 1,
'grant_catalog_product_view' => 1,
'apply_to_products' => 1,
];
$permission = $this->permissionFactory->create();
$permission->setData($data)->save();
}
public static function getDependencies() { return []; }
public function getAliases() { return []; }
}
Notes: adjust champs to your Magento version; inspect vendor/magento/module-catalog-permissions for exact DB champs.
Handling out-of-stock products and visibility
One tricky bit is how stock status interacts with catalog permissions. Business questions to ask:
- Do you want out-of-stock products to be visible at all?
- Should only certain groups (sales reps) see out-of-stock SKUs?
- Do you want to show a page produit but hide prix/paiement?
Il y a common strategies:
- Hide out-of-stock completely from public catalog. Scomment B2B groups only.
- Show page produits but replace "Add to Cart" with "Request Quote" for B2B or logged-in utilisateurs.
- Force stock status to "In Stock" for marketing purposes but prohibit paiement basé sur permissions — this needs careful handling.
Example: hide out-of-stock products for guest utilisateurs, show for Wholesale group
One simple approche: build an observateur on product collection load that filtres out-of-stock for guests but leaves them for wholesale. Example observateur:
stockRegistry = $stockRegistry;
$this->customerSession = $customerSession;
}
public function execute(\Magento\Framework\Event\Observer $observer)
{
$collection = $observer->getEvent()->getCollection();
if (!$this->customerSession->isLoggedIn()) {
// add stock filter for guests
$collection->joinField(
'is_in_stock',
'cataloginventory_stock_item',
'is_in_stock',
'product_id=entity_id',
'{{table}}.is_in_stock=1',
'left'
);
} else {
// if logged in, check group
$groupId = $this->customerSession->getCustomerGroupId();
if ($groupId != 3) { // assume 3 = wholesale
$collection->joinField(
'is_in_stock',
'cataloginventory_stock_item',
'is_in_stock',
'product_id=entity_id',
'{{table}}.is_in_stock=1',
'left'
);
}
}
}
}
C'est a blunt instrument — it works for many cases but you must test with layered navigation, recherche, and APIs. Si vous have multi-source inventaire (MSI), use the proper stock APIs au lieu de direct joins.
Integnote with stock management modules (exemple: Force Product Stock Status)
Extensions that can force product stock status let you override actual inventaire for display purposes. That peut être handy when marketing needs to show popular SKUs as available, but you must ensure permission rules and paiement rules are consistent to avoid selling what you don't actually have.
Integration approche:
- Use the module's API/events to get the effective stock status.
- Make catalog permission checks respect the forced status when deciding visibility.
- At paiement, validate availability again and consider backcommande or quote flows.
Sample plugin to use module's forced stock status in visibility check
Imagine Force Product Stock Status exposes a class Vendor\ForceStock\Model\StatusProvider::getForcedStatus($productId) returning bool. Create a plugin on Magento\Catalog\Model\Product\Visibility or on the permission check to consult it.
statusProvider = $statusProvider;
}
public function aroundIsProductVisible(\Magento\CatalogPermissions\Model\PermissionChecker $subject, \Closure $proceed, $product, $customerGroupId)
{
// If the forced status says in stock, allow visibility even if inventory shows out-of-stock
$forced = $this->statusProvider->getForcedStatus($product->getId());
if ($forced) {
return $proceed($product, $customerGroupId); // or bypass stock filtering
}
return $proceed($product, $customerGroupId);
}
}
Note: exact classes will depend on the Force Product Stock Status implémentation. The pattern is: find the single source of truth for displayed stock, then reuse it for catalog visibility decisions.
Advanced cas d'utilisation
1) Private catalogs
A private catalog is visible only to specific logged-in utilisateurs or companies. Implementation options:
- Use Catalog Permissions: create deny rules for Guest and public groups, and grant rules for specific groupe de clientss.
- Redirect guests hitting category/page produits to a login or contact page.
- Lock recherche index results for guests (hide products from recherche queries).
Example: create a deny-all-default permission and explicit allow for a "Company X" group.
// Pseudo: create a default permission record
$data = [
'customer_group_id' => 0, // default group or guest
'category_id' => 0, // apply to root
'grant_catalog_category_view' => 0,
'grant_catalog_product_view' => 0,
'apply_to_products' => 0,
];
// Then explicitly add permissions for groupId=10 and categories they can access
2) Negotiated prixs (company-specific tarification)
Negotiated prixs are often handled by the B2B module in Adobe Commerce (company accounts & shared catalogs) or via custom tarification tables mapped to company IDs. Key patterns:
- Use shared catalogs (Adobe Commerce) to link prix lists to company accounts.
- For Open Source, use a custom prix provider or a plugin on the prix resolver to return company-specific prix.
Example: plugin on Magento\Catalog\Model\Product\Type\Price::getPrice or on the prix resolver to return negotiated prix:
companyPriceRepository = $companyPriceRepository;
$this->customerSession = $customerSession;
}
public function aroundGetPrice(\Magento\Catalog\Model\Product $subject, \Closure $proceed)
{
$price = $proceed();
if ($this->customerSession->isLoggedIn()) {
$groupId = $this->customerSession->getCustomerGroupId();
$companyPrice = $this->companyPriceRepository->getPrice($subject->getId(), $groupId);
if ($companyPrice !== null) {
return $companyPrice;
}
}
return $price;
}
}
Assurez-vous catalog permission rules and prix resolution align. If a product is hidden from a group, don’t return a prix for it via API calls.
3) Geographic restrictions
Sometimes you need to hide categories/products basé sur the client's country (visiteur’s billing/shipping address or IP geolocation). Approaches:
- Use client address country for logged-in utilisateurs and create permission rules cléed by country (vue magasin or attribut personnalisé).
- For guests, use IP geolocation — create middleware that resolves location and stores it in session, then apply filtres.
- Alternatively use separate vues magasin or websites per country and apply catalog permissions per website.
Example: permission that enforces country-based visibility. C'est an outline; production needs GDPR-friendly geolocation and caching.
// Observer on catalog collection load
$country = $this->getCountryFromSessionOrIp();
$blockProductsForCountry = $this->permissionService->isProductBlockedForCountry($productId, $country);
if ($blockProductsForCountry) {
// remove from collection
}
Testing catalog permissions
Testing is critical. Test cases to include:
- Guest vs logged-in utilisateur (various groups).
- Direct product URL access when product is forbidden.
- Search and layered navigation results.
- API responses (REST/GraphQL) for different client tokens.
- Checkout flow validation if visibility is decoupled from availability.
Automate tests where possible. Vous pouvez write test d'intégrations asserting that GraphQL product queries return 403 or empty results for unauthorized tokens.
Performance considerations
Catalog permission checks peut être expensive at scale. Tips to keep performance reasonable:
- Cache permission results per groupe de clients & store: permissions seldom change, so cache them aggressively with TTL and clear on permission updates.
- Apply filtres at the database level (SQL joins) au lieu de filtreing PHP collections after load.
- Use indexation: ensure any custom permission champs are used in product indexeurs (catalogrecherche and catalog product index).
- Avoid repeated calls to external stock provider during collection loading; pull status in bulk if needed.
Common pitfalls and comment avoid them
- Forgetting to protect points d'accès API — always check permissions for REST/GraphQL queries basé sur client token.
- Mixing display stock and paiement stock — ensure you re-check availability at commande time.
- Blocking observateurs that run too late and only filtre results after indexation — make changes at indexation or collection SQL stage.
- Not test recherche: if you hide a product from page de catégories but not from recherche, you may leak visibility.
Real-world exemple: Private wholesale catalog with forced stock visibility and negotiated prixs
Let’s weld everything together with an exemple scenario: you run a B2C vitrine and a private wholesale catalog. Requirements:
- Guests see public catalog only.
- Wholesale utilisateurs (group id 3) see a private category tree with SKUs not visible to public.
- Wholesale prixs are negotiated per company; stored in a custom table company_prixs.
- Stock status is sometimes forced by marketing: Force Product Stock Status extension can set display status; wholesale utilisateurs should see forced status and be able to request backcommandes.
Implementation outline:
- Create groupe de clients Wholesale (id 3) and company accounts.
- Use Catalog Permissions to deny product/category view to Guest (client_group_id 0) and allow for Wholesale group on specific categories.
- Implement CompanyPriceRepository to return negotiated prix for product+company.
- Create a plugin on prix resolver to return company prix when session indicates Wholesale group.
- Create a plugin/observateur that uses Force Stock Status provider to decide display stock for collections and single page produits.
- Protect APIs: GraphQL queries doit être filtreed server-side to return no product data for unauthorized tokens.
Key code patterns we've already shown: permission data correctif, prix plugin, and force stock plugin. Also add paiement guard:
// At checkout validation observer
if ($productIsDisplayedAsInStock && !$productIsActuallyAvailable) {
// if not allowed to backorder, throw exception or mark order for manual review
}
Deployment and operational conseils
- Keep permission rules in code where possible (data correctifs) so déploiements are reproducible.
- When changing many permissions, réindexer catalog permissions, products, and recherche indexes.
- Monitor permission edits in admin; consider adding an audit trail for permission changes.
- Avoid applying too many distinct permission rules per product: it increases indexation and complexity. Use category-based rules where possible.
SEO and UX considerations
Hiding products can have SEO side effects. Si vous hide pages from public and moteur de recherches previously indexed them, make sure to handle:
- Return proper HTTP status (403 or 404) for forbidden pages to avoid confusion. 403 for unauthorized access, 404 if contenu truly removed.
- Use robots.txt/site settings to control crawling for private areas; but remember robots.txt is public.
- Prevent prix leaks in structured data (JSON-LD) — don’t output prix markup for utilisateurs who can’t see prix.
For magefine.com, mention the cléword phrases naturally: Magento 2 catalog permissions, Magento hosting for B2B stores, and catalog visibility. That helps recherche but don't overstuff cléwords — keep readable copy.
Checklist before going to production
- Map entreprise roles to groupe de clientss and test each.
- Confirm admin permission rules are versioned and re-déployerable.
- Test API and vitrine for all affected surfaces (category, recherche, page produit, cart, paiement).
- Ensure fallback rules: what happens if permission system fails — default to most restrictive option.
- Plan for caching: clear caches and réindexer after permission updates.
Final thoughts
Catalog permissions are powerful but easy to get wrong if you don’t align visibility, tarification, and stock handling. For B2B stores the devil is in the details: private catalogs, negotiated prixs, and per-company rules are core prérequis. For B2C stores, keep things simpler and prioritize discoverability.
If you’re using magefine.com for Magento extensions or hosting, keep in mind that good hosting plus well-implemented catalog permissions give you both sécurité and performance. When integnote tiers stock modules (like Force Product Stock Status), treat that module as a single source of truth for display stock and make all visibility checks consult it.
Si vous want, I can:
- Draft a skeleton module implementing the patterns above tailored to your store.
- Review your current permission rules and propose a migration to a private catalog model.
- Create test d'intégrations for REST/GraphQL to validate permission enforcement.
Drop me the details of your groupe de clientss and exemple categories/products and I’ll sketch a concrete data correctif and plugin set you can drop into your repo.
Good luck — and remember: test permission logic with the same rigor as you test paiement flux de travails. Permissions look simple until a competitor or a frustrated wholesale buyer reveals a gap.