← Back to blog

Adobe Commerce B2B Features: A Developer Overview

Rodrigo Biazzi

Adobe Commerce ships a set of B2B modules that cover the things wholesale buyers expect: company accounts with several users, customer specific catalogs and prices, quotes that go back and forth with a sales rep, purchase orders that need approval, and fast reordering by SKU. None of this exists in Magento Open Source. It comes as a separate Composer package that only installs on Adobe Commerce, and it touches checkout, pricing, customer accounts and the admin in ways a developer has to understand before building on top of it.

This article is a developer overview. It covers what each feature does, which modules implement it, how to enable it, where it adds load and what to watch when you write custom code on a B2B store.

Installing the B2B extension

The B2B features live in the magento/extension-b2b metapackage. It is available from repo.magento.com only to Adobe Commerce licenses, and its version has to match the Adobe Commerce release you run. Check the compatibility table in the release notes before you require it.

composer require magento/extension-b2b
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush

After install, the features are switched on per website under Stores > Configuration > General > B2B Features. Company accounts have to be enabled first because most of the other features depend on a buyer belonging to a company. Shared catalogs, negotiable quotes, quick order, requisition lists and purchase orders each have their own switch.

Company accounts

The Magento_Company module adds a company entity above the customer. A company has an admin user (the buyer who registered it), additional users, teams, a legal address, a sales representative from your admin users and a customer group that drives pricing. Company admins manage their own users and a role tree on the storefront. Each role grants or denies resources such as viewing orders, placing orders, using quotes or managing users.

For custom code, the most common need is finding out which company the current customer belongs to. The relation is exposed as a customer extension attribute, and the module has a service for loading the company itself.

<?php
declare(strict_types=1);

namespace Vendor\B2bTools\Model;

use Magento\Company\Api\CompanyManagementInterface;
use Magento\Company\Api\Data\CompanyInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;

class CompanyResolver
{
    public function __construct(
        private readonly CustomerRepositoryInterface $customerRepository,
        private readonly CompanyManagementInterface $companyManagement
    ) {
    }

    public function getCompanyId(int $customerId): ?int
    {
        $customer = $this->customerRepository->getById($customerId);
        $attributes = $customer->getExtensionAttributes()?->getCompanyAttributes();
        $companyId = $attributes?->getCompanyId();

        return $companyId ? (int) $companyId : null;
    }

    public function getCompany(int $customerId): ?CompanyInterface
    {
        return $this->companyManagement->getByCustomerId($customerId);
    }
}

Keep in mind that a company user can be inactive while the customer record still exists. If your logic grants something based on company membership, check the company status too. Blocked or rejected companies should not get B2B prices or payment options.

Shared catalogs

Magento_SharedCatalog controls which products and categories a company can see and what it pays. There is one public shared catalog for guests and retail customers, and any number of custom ones assigned to companies. Under the hood, each shared catalog is tied to a customer group. Product visibility is enforced through category permissions, which is another Adobe Commerce feature, and custom prices are stored as tier prices for that customer group.

That design has two consequences you will feel in development:

  • Saving a shared catalog with many products writes a lot of tier price and permission rows. The work is pushed to message queue consumers such as sharedCatalogUpdatePrice and sharedCatalogUpdateCategoryPermissions. If consumers are not running, admin changes look like they did nothing. Run bin/magento queue:consumers:list to see the full list on your version and make sure cron or a supervisor runs them.
  • Any custom code that queries products directly, such as a feed, a custom listing or a GraphQL resolver, can bypass category permissions and leak products a company should not see. Go through the standard collection and repository layers, and test as a logged in company user.

Shared catalogs also change how full page cache varies. Pages are cached per customer group, so a store with hundreds of companies on separate shared catalogs ends up with many cache variations of the same category page. If performance matters, group companies into a small number of catalogs instead of one catalog per company.

Negotiable quotes

Magento_NegotiableQuote lets a buyer turn a cart into a quote request. The seller edits prices and quantities in the admin, adds comments and sends it back. When both sides agree, the buyer checks out with the negotiated prices locked. The quote is a regular quote record with an extra negotiable quote table linked to it, plus a history log of every change.

Because the negotiated price must survive checkout, the module affects totals collection. Custom totals collectors and plugins on price calculation need testing against negotiable quotes specifically. A fee or discount that works in a normal cart can be applied twice or ignored when a quote is converted, depending on where you hook in.

Purchase orders and approval rules

With purchase orders enabled, placing an order from a company account creates a purchase order first. Approval rules defined by the company admin decide whether it needs approval, for example when the order total is above a limit or a specific shipping method is used. Only after approval does it become a sales order. The modules involved are Magento_PurchaseOrder and Magento_PurchaseOrderRule.

For integrations this matters a lot. An ERP connector that listens for orders will not see anything until approval happens, and some payment methods behave differently because payment is captured at conversion time, not when the buyer clicks the button. Payment on account, provided by Magento_CompanyCredit, fits this flow well because it charges a credit limit instead of a card.

Quick order and requisition lists

Magento_QuickOrder gives buyers a page where they type or paste SKUs and quantities, or upload a CSV, and add everything to the cart at once. Magento_RequisitionList lets them save named lists of products for repeat purchases. Both are simple on the surface, but they rely on SKU lookups. If your catalog uses configurable products, buyers have to enter the child SKU. If you have custom product types or product options, test that they can be added through these flows, since they skip the normal product page.

GraphQL and headless B2B

Recent 2.4 releases expose most B2B features through GraphQL: company data and users, requisition lists, negotiable quotes and purchase orders. If you build a headless storefront, check the GraphQL reference for your exact version, because coverage has grown release by release. The schema files live in the B2B modules that end in GraphQl, and you can extend them the same way as core schemas. The general approach is the same as in a custom GraphQL query resolver.

What to watch when building on a B2B store

  1. Customer group is overloaded. Shared catalogs own the customer group of each company. Do not reuse customer groups for unrelated logic such as tax classes or marketing segments without checking how that interacts with catalog assignment.
  2. Consumers must run. Shared catalog updates, quote price updates and purchase order processing all depend on queue consumers. Treat consumer health as part of monitoring. See RabbitMQ consumers in Magento 2 for the setup.
  3. Permissions are enforced in many places. Company roles are checked in controllers and in the storefront UI. A custom page that lists orders or invoices should check company permissions as well, not only that the customer is logged in.
  4. Indexing grows with catalogs. More customer groups means more rows in price and permission indexes. Indexers in update by schedule mode with a working cron are a must. The details are in indexer modes explained.
  5. Third party extensions. Many marketplace extensions were never tested with B2B. Check checkout, pricing and customer account extensions against company users before you commit to them.

When the B2B suite fits

The B2B modules fit stores where buyers are companies with several people ordering, where prices are negotiated per account and where approval flows are part of how customers buy. If the requirement is only a wholesale price for a group of customers, Open Source tier prices and customer groups may be enough, and that avoids a lot of moving parts. If you are still deciding on the edition, the broader comparison is in Magento Open Source vs Adobe Commerce.

FAQ

Can I install the B2B extension on Magento Open Source?

No. The magento/extension-b2b package is only available to Adobe Commerce licenses and depends on Commerce modules such as category permissions. Open Source stores need third party B2B extensions or custom development.

Why do shared catalog changes not show up on the storefront?

Shared catalog price and permission updates are processed by message queue consumers. If the consumers or cron are not running, the changes stay queued. Check the consumers, then the indexers and the cache.

How do I get the company of a logged in customer in code?

Read the company_attributes customer extension attribute and call getCompanyId(), or use CompanyManagementInterface::getByCustomerId() to load the company entity.

Do purchase orders affect ERP integrations?

Yes. With purchase orders enabled, a sales order only exists after approval. Integrations that listen for new orders will only see the order at that point, so plan the flow with that delay in mind.

I am available for freelance Magento and Adobe Commerce work, including B2B builds and integrations. If you need help with this, get in touch.

Related articles