Search by

mar-pod-b2b / module-b2b-sales-representative

Company sales-representative assignment and audited assisted selling for Marpod B2B Advanced.

Maintainers

Package info

gitlab.com/mar-pod-b2b/module-b2b-sales-representative

Issues

Type:magento2-module

pkg:composer/mar-pod-b2b/module-b2b-sales-representative

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

1.1.10 2026-08-24 16:09 UTC

README

Company-to-admin sales representative assignment, a portfolio-restricted Companies grid, internal notes/tasks, and an audited assisted-selling service that never inherits the customer's session.

Status

Full assignment/portfolio engine and Admin UI shipped.

Implemented foundation

  • declarative schema: marpod_b2b_sales_rep_assignment (company × admin_user, unique, with is_primary/is_active flags), marpod_b2b_sales_rep_note (company-scoped internal notes), marpod_b2b_sales_rep_task (company-scoped tasks with a due date and done flag);
  • SalesRepPortfolioResolverInterface::getAssignedCompanyIds()/isAssignedToCompany() — the single source of truth for "which companies is this admin responsible for";
  • restricted Companies grid: RestrictCompanyGridToPortfolioPlugin filters the base Marpod_B2bCompany admin grid to an admin's assigned companies unless they hold the Marpod_B2bSalesRepresentative::view_all_companies ACL resource. It targets the shared Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult base class (guarded to the Companies grid at runtime via the collection's main table) rather than the MarpodB2bCompanyGridCollection virtualType name directly — declaring a <virtualType> for that name in one module and a <type> plugin target of the same name in another was found to silently break DI compilation of the virtualType (fixed in 1.1.1; see the module's git history for RestrictCompanyGridToPortfolioPlugin if implementing a similar cross-module grid plugin);
  • AssistedSellingServiceInterface::createCartForCustomer()/createQuoteForCustomer() — creates a cart or quote "on behalf of" a customer without ever inheriting their session: it takes an explicit $customerId and drives session-independent primitives directly (CartManagementInterface::createEmptyCartForCustomer()/getCartForCustomer(), QuoteRepositoryInterface, the frozen Marpod_B2bQuote\Api\QuoteItemRepositoryInterface) — never the session-bound Marpod_B2bQuote\Api\QuoteManagementInterface::createFromCart(). Authorization goes through the portfolio resolver first (LocalizedException if the acting admin is not assigned to the customer's company);
  • every assisted action is logged through the existing, frozen Marpod\B2bCore\Api\Audit\AuditLoggerInterface — with actorCustomerId = null (the same "null = admin/system" convention PurchaseRequestWorkflowInterface::transition() already uses) and the acting admin's identity plus impersonation: true recorded in the event's free- form metadata, since AuditEventInterface has no dedicated admin-actor field.

Admin UI

Marpod_B2bSalesRepresentative::manage (marpod_b2b_sales_representative/assignment/index) provides assignment management with native Magento General, Notes, and Tasks tabs. Notes and Tasks use independent filterable and pageable embedded grids; their add forms are separated below the grids, and every related POST action restores the originating tab.

Assignments are an Admin/CRM concern. Their PHP service contracts remain the source of truth for integrations; a storefront GraphQL surface is intentionally not exposed because admin-user identity and internal notes/tasks are not customer-facing data. ACL-protected REST CRUD/search for assignments is available under /V1/marpod-b2b/sales-rep-assignments; notes and tasks remain internal until a concrete CRM consumer and its field-level permissions are defined.

Requirements

  • PHP 8.5
  • Magento 2.4.9-compatible framework packages
  • mar-pod-b2b/module-b2b-core, mar-pod-b2b/module-b2b-company, mar-pod-b2b/module-b2b-quote

Installation

Run from the Magento project root:

composer require mar-pod-b2b/module-b2b-sales-representative:^1.1
bin/magento module:enable Marpod_B2bSalesRepresentative
bin/magento setup:upgrade
bin/magento setup:di:compile
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 (section 9.7).

Unit tests:

vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist packages/module-b2b-sales-representative/Test/Unit

Admin smoke test:

cd packages/module-b2b-sales-representative/Test/Playwright
npm install
npm test

Limitations

  • no email/notification dispatch for due-soon tasks;
  • a company can have multiple is_primary assignments simultaneously — the schema does not enforce exactly one primary rep per company, only uniqueness per company+admin pair.

Uninstallation

Do not remove the package while another installed module requires it. The marpod_b2b_sales_rep_* tables contain business data and are retained when the module is disabled or its package is removed; removing it also removes the Companies-grid restriction, so every admin sees every company again.