mar-pod-b2b / module-b2b-company
Company accounts, members, roles, permissions, invitations, and audit for Marpod B2B.
Package info
gitlab.com/mar-pod-b2b/module-b2b-company
Type:magento2-module
pkg:composer/mar-pod-b2b/module-b2b-company
Requires
- php: ^8.5
- magento/framework: ^103.0
- magento/module-backend: ^102.0
- magento/module-customer: ^103.0
- magento/module-email: ^101.1
- magento/module-graph-ql: ^100.4
- magento/module-store: ^101.1
- magento/module-ui: ^101.2
- magento/module-webapi: ^100.4
- 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 08:06:02 UTC
README
Company accounts, members, roles, permissions, invitations, and audit.
Implemented foundation
- website-scoped company aggregate with pending, active, rejected, and blocked statuses;
- declarative
marpod_b2b_companytable with website and VAT uniqueness; - public company data and repository service contracts;
- validated persistence and SearchCriteria-based listing;
- Admin-only REST CRUD endpoints protected by
Marpod_B2bCompany::manage. - company-scoped roles and permission assignments;
- one company membership per customer and website;
- deny-by-default authorization integrated with B2B Core.
- email-bound, expiring, one-time company invitations storing only a SHA-256 token hash;
- persistent company audit log with scalar metadata filtering and secret/token key rejection.
- Admin grid and company editor under
B2B > Companies, protected by theMarpod_B2bCompany::manageACL resource. - customer account company profile resolved from the authenticated membership and
website, with edits gated by
company.profile.manage. - authorized role-management service contract with permission allow-listing, transactional updates, assignment-safe deletion, and persistent audit events.
- Admin company-team overview for members, roles, permission counts, and pending invitations, available from the company editor.
- context-aware
Add Memberaction on the Members tab, assigning an existing website customer by email to a selected company role. - context-aware
Invite Memberaction on the Invitations tab, with a native Admin form, configurable expiration, transactional email, one-time acceptance link, pending-invitation grid, and revocation confirmation. - POST-only Admin actions for member role changes, member removal, and invitation revocation, with company-bound validation, last-admin protection, and audit.
- Admin role create/edit form with the complete permission allow-list and safe deletion restricted to roles that have no assigned members.
REST and GraphQL
Administrative REST routes below /rest/V1/marpod-b2b/companies require the
Marpod_B2bCompany::manage ACL resource. They expose company lookup/list/save/delete,
roles, and invitation create/list/revoke operations.
Authenticated customers can query marpodB2bCompany through GraphQL for their current company's
profile, team members, roles, and pending invitations — the headless/PWA equivalent of the Admin
company-team overview. A company administrator can send an invitation with the
marpodB2bInviteCompanyMember mutation.
The company is resolved from the customer token and current website; callers cannot submit a
company ID, and a customer with no company assignment gets an explicit authorization error rather
than another company's data.
Requirements
- PHP 8.5
- Magento 2.4.9-compatible framework packages
- dependencies declared in
composer.json
Installation
Run from the Magento project root:
composer require mar-pod-b2b/module-b2b-company:@dev
bin/magento module:enable Marpod_B2bCompany
bin/magento setup:upgrade
bin/magento cache:clean
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.
Add unit and integration tests together with each implemented behavior. Run Composer validation, PHP syntax checks, coding standards, affected tests, and dependency-injection compilation where applicable before release.
Unit tests:
php vendor/bin/phpunit --bootstrap packages/module-b2b-company/Test/Unit/bootstrap.php \
packages/module-b2b-company/Test/Unit
Integration tests (run in the Magento PHP container with the integration database available):
php vendor/bin/phpunit \
--bootstrap packages/module-b2b-company/Test/Integration/bootstrap.php \
/var/www/html/packages/module-b2b-company/Test/Integration/Model/CompanyRepositoryTest.php
The REST API is intentionally restricted to administrators at this stage. Storefront
company access (both the customer account panel and the marpodB2bCompany GraphQL query)
resolves the company from the authenticated customer and current website. It never trusts a
company identifier supplied by the browser.
REST/GraphQL API tests (real HTTP against a running instance — routing, authentication, ACL
enforcement, and cross-company 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-company/Test/Api/Rest/CompanyRestTest.php
vendor/bin/phpunit -c dev/tests/api-functional/phpunit_graphql.xml \
packages/module-b2b-company/Test/Api/GraphQl/CurrentCompanyGraphQlTest.php
Playwright covers the storefront guest path and the Admin company editor, tabs, grids, roles, permission selection, and the complete add-member workflow. The add-member test creates an isolated customer and role, assigns the customer from the Members tab, verifies the grid, and removes all fixtures during teardown.
Run Playwright tests:
cd packages/module-b2b-company/Test/Playwright
npm install
npx playwright install chromium
npm test
Set PLAYWRIGHT_BASE_URL when Magento is not available at http://localhost:8090.
Invitation tokens are created by CompanyInvitationInterface::issue() and delivered by
CompanyInvitationManagementInterface::invite() through Magento's transactional email
transport. The raw token is never persisted or written to audit metadata. Acceptance is
restricted to a logged-in customer whose normalized email and website match the invitation,
and succeeds only once. Acceptance links are always built from the website store's frontend
base URL, never from the current Admin request. With Marpod_B2bClosedStore enabled, a guest
is redirected to customer login and then returned to the invitation URL. A transport failure
revokes the newly created invitation.
Uninstallation
Do not remove the package while another installed module requires it. The
marpod_b2b_company table contains business data and is retained when the module is
disabled or its package is removed. Export and explicitly archive or delete company
records according to the merchant's retention policy before removing database data.