Implement Magento 2 B2B commerce features including company accounts, shared catalogs, negotiated quotes, and purchase order workflows for enterprise.
The B2B Commerce Landscape
B2B eCommerce operates fundamentally differently from B2C. Buyers need company accounts with role-based permissions, negotiated pricing, purchase order approval workflows, and quick-order capabilities. Magento 2 Commerce (Adobe Commerce) includes a comprehensive B2B module that addresses these requirements out of the box.
This guide covers practical implementation of the core B2B features, including configuration, customization, and performance considerations for Magento 2 stores handling enterprise B2B workloads.
Company Account Structure
B2B buyers operate within organizational hierarchies. The Company Account feature in Magento 2 models these relationships:
Company Admin: Creates and manages the company account, assigns roles, and manages users.
Roles and Permissions: Define what each team member can do - browse catalog, add to cart, place orders, approve purchases, manage company users.
Programmatic Company Creation
use Magento\Company\Api\CompanyRepositoryInterface;
use Magento\Company\Api\Data\CompanyInterfaceFactory;
class CompanyService
{
public function __construct(
private CompanyRepositoryInterface $companyRepository,
private CompanyInterfaceFactory $companyFactory
) {}
public function createCompany(array $data): int
{
$company = $this->companyFactory->create();
$company->setCompanyName($data['name']);
$company->setCompanyEmail($data['email']);
$company->setStreet($data['street']);
$company->setCity($data['city']);
$company->setCountryId($data['country']);
$company->setPostcode($data['postcode']);
$company->setTelephone($data['phone']);
$company->setSuperUserId($data['admin_user_id']);
$company->setStatus(1); // Active
$saved = $this->companyRepository->save($company);
return (int) $saved->getId();
}
}
Role Configuration
use Magento\Company\Api\RoleRepositoryInterface;
use Magento\Company\Api\Data\RoleInterfaceFactory;
class RoleService
{
private const PERMISSIONS = [
'Magento_Company::index' => 'allow',
'Magento_Sales::place_order' => 'allow',
'Magento_Sales::view_orders' => 'allow',
'Magento_NegotiableQuote::manage' => 'allow',
'Magento_PurchaseOrder::approve' => 'deny',
];
public function createBuyerRole(int $companyId): void
{
$role = $this->roleFactory->create();
$role->setCompanyId($companyId);
$role->setRoleName('Buyer');
$role->setPermissions($this->buildPermissionTree(self::PERMISSIONS));
$this->roleRepository->save($role);
}
}
Shared Catalogs
Shared Catalogs allow you to create custom pricing tiers visible only to specific companies. This replaces the need for complex catalog price rules.
Creating a Custom Shared Catalog
In the admin panel: Catalog > Shared Catalogs > Add Shared Catalog
Programmatically:
use Magento\SharedCatalog\Api\SharedCatalogRepositoryInterface;
use Magento\SharedCatalog\Api\Data\SharedCatalogInterfaceFactory;
$catalog = $this->sharedCatalogFactory->create();
$catalog->setName('Gold Tier Partners');
$catalog->setDescription('Preferred pricing for gold tier B2B partners');
$catalog->setType(SharedCatalogInterface::TYPE_CUSTOM);
$catalog->setTaxClassId(2);
$this->sharedCatalogRepository->save($catalog);
Assigning Custom Prices
use Magento\SharedCatalog\Api\ProductManagementInterface;
// Assign products to shared catalog
$this->productManagement->assignProducts(
$sharedCatalogId,
$productSkus
);
// Set custom tier prices
$tierPrice = $this->tierPriceFactory->create();
$tierPrice->setCustomerGroupId($sharedCatalog->getCustomerGroupId());
$tierPrice->setQty(1);
$tierPrice->setValue(89.99); // Custom price for this catalog
$tierPrice->setWebsiteId(0);
$this->tierPriceStorage->update([$tierPrice]);
Negotiable Quotes
The negotiable quote workflow enables buyers to request custom pricing on orders, with back-and-forth negotiation between buyer and sales representative.
Quote Workflow
- Buyer adds items to cart and requests a quote
- Sales rep receives notification and reviews the request
- Sales rep adjusts pricing (line item or total discount)
- Buyer reviews, accepts, or counters
- Accepted quote converts to an order
Custom Quote Validation
use Magento\NegotiableQuote\Api\NegotiableQuoteManagementInterface;
class QuoteApprovalService
{
public function validateAndApprove(int $quoteId, float $maxDiscount): bool
{
$quote = $this->quoteRepository->get($quoteId);
$negotiableQuote = $quote->getExtensionAttributes()
->getNegotiableQuote();
$originalTotal = $quote->getBaseGrandTotal();
$negotiatedTotal = $negotiableQuote->getNegotiatedPriceValue();
$discountPercent = (($originalTotal - $negotiatedTotal) / $originalTotal) * 100;
if ($discountPercent > $maxDiscount) {
$negotiableQuote->setStatus(
NegotiableQuoteInterface::STATUS_DECLINED
);
$this->quoteRepository->save($quote);
return false;
}
$this->negotiableQuoteManagement->adminSend($quoteId);
return true;
}
}
Purchase Order Approval Workflows
Enterprise buyers often require multi-level approval before a purchase order is placed. Magento 2 B2B supports configurable approval rules:
Approval Rule Configuration
// Create approval rule: orders over $5,000 need manager approval
$rule = $this->approvalRuleFactory->create();
$rule->setName('Large Order Approval');
$rule->setDescription('Orders exceeding $5,000 require manager approval');
$rule->setIsActive(true);
$rule->setCompanyId($companyId);
$rule->setConditionsSerialized(json_encode([
'type' => 'Magento\PurchaseOrderRule\Model\Rule\Condition\Combine',
'conditions' => [
[
'type' => 'Magento\PurchaseOrderRule\Model\Rule\Condition\Address',
'attribute' => 'grand_total',
'operator' => '>',
'value' => 5000,
],
],
]));
$rule->setApproverRoleIds([$managerRoleId]);
$this->approvalRuleRepository->save($rule);
Quick Order and Requisition Lists
B2B buyers frequently reorder the same products. Requisition Lists function as persistent shopping lists:
use Magento\RequisitionList\Api\RequisitionListRepositoryInterface;
$list = $this->requisitionListFactory->create();
$list->setName('Monthly Office Supplies');
$list->setCustomerId($customerId);
$list->setDescription('Standard monthly order for building A');
$this->requisitionListRepository->save($list);
// Add items to requisition list
foreach ($skus as $sku => $qty) {
$item = $this->requisitionListItemFactory->create();
$item->setSku($sku);
$item->setQty($qty);
$item->setListId($list->getId());
$this->requisitionListItemRepository->save($item);
}
Performance Considerations for B2B
B2B catalogs often contain hundreds of thousands of SKUs with complex pricing rules. Performance optimization is critical:
- Shared Catalog Indexing: Shared catalog price indexing can be resource-intensive. Run indexers in
schedulemode with cron and allocate dedicated worker processes. - Company Structure Caching: Cache the company hierarchy and permission tree in Redis to avoid database queries on every page load.
- Quote Calculation: Negotiable quotes with many line items require optimized price calculation. Pre-calculate and cache tier prices.
- Elasticsearch Tuning: B2B search queries often include SKU-based searches and attribute filtering. Configure Elasticsearch with custom analyzers for SKU patterns.
Integration Patterns
B2B implementations typically integrate with ERP systems. Use Magento's async REST API for bulk operations:
curl -X POST "https://store.example.com/rest/async/bulk/V1/products" \
-H "Authorization: Bearer admin_token" \
-H "Content-Type: application/json" \
-d '[
{"product": {"sku": "PART-001", "name": "Widget A", "price": 12.99}},
{"product": {"sku": "PART-002", "name": "Widget B", "price": 15.99}}
]'
The async API queues operations in RabbitMQ, processes them in the background, and provides a bulk operation status endpoint for monitoring progress.
Best Practices
- Start with company account structure and roles before implementing complex features
- Use shared catalogs for pricing tiers instead of complex catalog price rules
- Test approval workflows with real business scenarios from your sales team
- Implement ERP sync as an asynchronous, event-driven process
- Monitor server performance closely during B2B catalog reindexing
B2B commerce on Magento 2 requires careful planning around organizational structures, pricing models, and approval workflows. The platform provides the building blocks, but success depends on mapping business requirements accurately to technical implementation.
Frequently asked questions
How do company accounts and roles work in Magento 2 B2B?
Magento 2 Commerce (Adobe Commerce) models a buyer organization as a company account. The Company Admin creates and manages the account, assigns roles and manages users. Each role defines what a team member can do, such as browse the catalog, add to cart, place orders, approve purchases or manage company users, through permissions like Magento_Sales::place_order set to allow or Magento_PurchaseOrder::approve set to deny.
How do I create a Magento 2 B2B company programmatically?
Inject CompanyRepositoryInterface and CompanyInterfaceFactory from the Magento Company API, create a company object and set its name, email, street, city, country ID, postcode and telephone. Calling setSuperUserId with the admin user ID assigns the company admin, and setStatus(1) marks the company active. Saving through the company repository returns the new company ID.
How do I set custom prices for specific B2B companies in Magento 2?
Use a shared catalog, which creates a pricing tier visible only to specific companies and replaces complex catalog price rules. Create a custom one in the admin under Catalog > Shared Catalogs > Add Shared Catalog, or programmatically with the type TYPE_CUSTOM. Assign products with assignProducts from ProductManagementInterface, then set tier prices against the shared catalog's customer group ID and save them through the tier price storage.
How does the negotiable quote workflow work in Magento 2 B2B?
A buyer adds items to the cart and requests a quote, and a sales rep is notified and reviews the request. The rep adjusts pricing with a line item or total discount, the buyer reviews, accepts or counters, and an accepted quote converts to an order. Custom validation can compute the discount percent against the base grand total, decline quotes above a maximum discount and send the rest back to the buyer with adminSend.
How do I require manager approval for large purchase orders in Magento 2?
Create a purchase order approval rule for the company with a condition on grand_total, for example greater than 5000, and set the manager role as approver with setApproverRoleIds. Orders over $5,000 then need manager approval before they are placed. Test approval workflows with real business scenarios from the sales team.
What are requisition lists in Magento 2 B2B?
Requisition lists are persistent shopping lists for B2B buyers who frequently reorder the same products, for example a Monthly Office Supplies list. A list is created through RequisitionListRepositoryInterface with a name, customer ID and description. Items are then added by SKU and quantity against the list ID.
How do I keep Magento 2 B2B fast with a large catalog?
Run indexers in schedule mode with cron and give them dedicated worker processes, because shared catalog price indexing is resource-intensive on catalogs with hundreds of thousands of SKUs. Cache the company hierarchy and permission tree in Redis to avoid database queries on every page load, and pre-calculate and cache tier prices for negotiable quotes with many line items. Configure Elasticsearch with custom analyzers for SKU patterns, and watch server performance closely during reindexing.
How should Magento 2 B2B integrate with an ERP system?
Run ERP sync as an asynchronous, event-driven process through Magento's async REST API for bulk operations, for example a POST of an array of products to /rest/async/bulk/V1/products. Magento queues those operations in RabbitMQ, processes them in the background and provides a bulk operation status endpoint for monitoring progress.
Read how we handle it in DevOps for e-commerce.
Related Articles
How to Boost Magento 2 Performance in a Few Easy Steps
Magento 2 delivers incredible flexibility for eCommerce, but without proper optimization it can become sluggish. This guide walks through ten proven DevOps strategies to dramatically speed up your store, from PHP upgrades and full-page caching to Varnish, Redis, CDN configuration, and ongoing code audits.
MagentoHow to Upgrade Magento 2 from 2.4.7 to 2.4.8
Keeping Magento current is critical for security, performance, and compatibility. This step-by-step guide walks developers through upgrading from Magento 2.4.7 to 2.4.8, covering system requirements, pre-upgrade checks, Git workflow, Composer commands, and post-upgrade validation.
MagentoHow to Configure Varnish for Magento So It Stops Caching the Wrong Thing
Varnish in front of Magento serves anonymous pages without touching PHP. The wrong Varnish in front of Magento serves one shopper's cart to everybody, which is worse than having no cache at all. Magento generates its own configuration and most of the work is using that rather than something copied from a forum. This covers what the generated file refuses to cache and why, how invalidation actually works as a ban rather than a purge, the two ways it silently fails, and how to prove a page came from cache.