Search by

mar-pod-b2b / module-b2b-company

Company accounts, members, roles, permissions, invitations, and audit for Marpod B2B.

Maintainers

Package info

gitlab.com/mar-pod-b2b/module-b2b-company

Issues

Type:magento2-module

pkg:composer/mar-pod-b2b/module-b2b-company

Transparency log

Statistics

Installs: 1

Dependents: 16

Suggesters: 0

Stars: 0

1.11.9 2026-08-23 18:18 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_company table 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 the Marpod_B2bCompany::manage ACL 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 Member action on the Members tab, assigning an existing website customer by email to a selected company role.
  • context-aware Invite Member action 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.