mar-pod-b2b / module-b2b-core
Shared contracts and extension infrastructure for Marpod B2B modules.
Package info
gitlab.com/mar-pod-b2b/module-b2b-core
Type:magento2-module
pkg:composer/mar-pod-b2b/module-b2b-core
Requires
- php: ^8.5
- magento/framework: ^103.0
- magento/module-backend: ^102.0
- magento/module-config: ^101.2
This package is auto-updated.
Last update: 2026-08-21 21:44:55 UTC
README
Shared contracts and extension infrastructure for independently installable Marpod B2B modules.
The package belongs to the mandatory B2B Base product. It intentionally contains no company persistence, pricing rules, quote workflow, payment method, or storefront-private data.
Features
- capability registry composed through Magento dependency injection;
- B2B actor, company, website, and currency context contract;
- versioned pricing-context contract for guest, customer, and company scopes;
- deny-by-default authorization service backed by optional permission providers;
- audit event and audit logger contracts with a safe no-op default;
- bulk-cart request and per-item result contracts shared by Quick Order, CSV upload, and requisition lists;
- independent B2B Admin menu (pinned directly under Dashboard), configuration section, and ACL resources;
- a shared
MarpodB2bBulkCartItemResultGraphQL type (etc/schema.graphqls) — declared here, not by any single feature module, since Purchase Approval's and Requisition List'sconvertToCart/addToCartmutations both return this shape and GraphQL types can't be declared twice across modules.
Requirements
- PHP 8.5
- Magento Open Source or Adobe Commerce 2.4.9
- Magento Backend and Config modules
Installation
Run from the Magento project root:
composer require mar-pod-b2b/module-b2b-core:^1.0@dev
bin/magento module:enable Marpod_B2bCore
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:clean
During development, branch 1.x is exposed as 1.0.x-dev through the Composer branch alias.
Capability registration
An optional module registers its technical availability through DI:
<type name="Marpod\B2bCore\Model\CapabilityRegistry">
<arguments>
<argument name="capabilities" xsi:type="array">
<item name="quick_order" xsi:type="boolean">true</item>
</argument>
</arguments>
</type>
Use CapabilityRegistryInterface::isAvailable() only to check whether code is installed. Configuration and user authorization are separate decisions.
Authorization providers
Feature modules add narrow PermissionProviderInterface implementations to the authorization pool:
<type name="Marpod\B2bCore\Model\Authorization\AuthorizationService">
<arguments>
<argument name="permissionProviders" xsi:type="array">
<item name="company" xsi:type="object">Vendor\Module\Model\CompanyPermissionProvider</item>
</argument>
</arguments>
</type>
Unsupported permissions and an empty provider pool are denied. Mutating application services must perform authorization themselves; a controller check alone is insufficient.
Pricing context
PricingContextInterface explicitly identifies the pricing scope and keeps the acting customer separate from the company whose contract price is used. Supported scope types are:
guestwith no scope ID;customerwith the actor customer ID;companywith the company ID.
The context also contains website and ISO 4217 currency codes so consumers can build safe cache keys without leaking company-specific data through FPC or Varnish.
Audit
The default NullAuditLogger prevents optional audit storage from becoming an availability dependency. A persistence module should replace AuditLoggerInterface and store only allow-listed metadata. Secrets, tokens, payment details, and unnecessary customer data must never be placed in audit metadata.
Tests
From a Magento project where this package is installed as a path repository:
vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist \
packages/module-b2b-core/Test/Unit
vendor/bin/phpunit -c dev/tests/integration/phpunit.xml.dist \
--bootstrap packages/module-b2b-core/Test/Integration/bootstrap.php \
packages/module-b2b-core/Test/Integration
Also run Composer validation, PHP syntax checks, Magento coding standards, bin/magento setup:upgrade, and bin/magento setup:di:compile before release.
Shared API test infrastructure
Test/Api provides base classes any B2B module can extend for REST/GraphQL integration tests
under dev/tests/api-functional (real HTTP calls against a running instance — routing,
authentication, and ACL enforcement all execute for real, unlike a plain service-layer test):
Marpod\B2bCore\Test\Api\AbstractRestTestCase(extendsWebapiAbstract) —getAdminToken()for the suite's configured full-access admin account;getRestrictedAdminToken(array $allowedAclResources), a throwaway role+user scoped to exactly the given ACL resources, for proving a route rejects a token that lacks them;restApiCall($path, $httpMethod, $token, $arguments)to make the call. Both admin fixtures are cleaned up intearDown()— always delete the role along with the user if you ever bypass this helper and create one directly: an orphanedauthorization_rolerow breaksMagento\Framework\Acl\Builderfor the entire site, not just the test suite, until it's removed.Marpod\B2bCore\Test\Api\AbstractGraphQlTestCase(extendsGraphQlAbstract) —getCustomerAuthHeaders($email, $password)for an authenticated customer GraphQL call.
Company-scoped fixture building (a company, and optionally a customer assigned to it) lives in
Marpod\B2bCompany\Test\Api\CompanyFixtureTrait — any module that already depends on
mar-pod-b2b/module-b2b-company (nearly all of them) can use it directly.
createCustomerFixtureWithPermissions() on the same trait creates a customer whose role carries
exactly the given CompanyPermissionInterface codes, for tests exercising a specific permission
rather than plain company membership.
Run with the project's local (non-.dist) dev/tests/api-functional/phpunit_rest.xml /
phpunit_graphql.xml, e.g.:
vendor/bin/phpunit -c dev/tests/api-functional/phpunit_rest.xml \
packages/module-b2b-company-credit/Test/Api/Rest/CreditAccountRestTest.php
vendor/bin/phpunit -c dev/tests/api-functional/phpunit_graphql.xml \
packages/module-b2b-company-credit/Test/Api/GraphQl/CompanyCreditGraphQlTest.php
Two environment quirks worth knowing before adding more tests here:
- ACL-insufficient tokens get HTTP 401, not 403. Magento's webapi framework maps both "no token" and "token lacks the resource" to 401 — distinguish the latter by asserting the response body names the missing resource, not by status code alone.
- Every
Api\Data\*Interfacegetter/setter needs a real docblock with an explicit@returnon its own line. Three patterns are silently misparsed by Magento's webapi reflection: no docblock at all ("Each method must have a doc block"), a single-line/** @param int $x @return $this */combining both tags, and a single-line docblock with description text before the tag (e.g./** Get X. @return int */). Only a bare single-tag one-liner (/** @return int */) or a full multi-line docblock (each tag on its own line) survives — these bugs surface only when something actually serializes that type through webapi/GraphQL, which is exactly what these tests do for the first time for several interfaces. - GraphQL mutations must be called with
graphQlMutation(), notgraphQlQuery(), inAbstractGraphQlTestCase-based tests —graphQlQuery()sends a GET, and Magento rejects any mutation operation sent that way ("Mutation requests allowed only for POST requests") regardless of the query string's own contents. A plain read query works fine through either method, so this only bites the first time a test file adds its first mutation call.
Cache and operations
The module creates no public cache entries, indexers, cron jobs, queues, or database tables. Feature modules remain responsible for precise cache tagging and invalidation of their own data.
Limitations
- permission providers and persistent audit storage are supplied by feature modules;
- this package does not resolve the current company or pricing context from a Magento session;
- bulk-cart contracts do not implement product loading, inventory validation, or quote mutation.
Uninstallation
Disable and remove dependent Marpod B2B modules first:
bin/magento module:disable Marpod_B2bCore
composer remove mar-pod-b2b/module-b2b-core
bin/magento setup:upgrade
bin/magento cache:clean
The module owns no database tables, so uninstalling it does not delete business data. Composer prevents removal while another installed package requires it.