Install the Marketplace Merchant Portal Core feature

Edit on GitHub

This document describes how to install the Marketplace Merchant Portal Core feature.

Install feature core

Follow the steps below to install the Merchant Portal Core feature core.

Prerequisites

Install the required features:

NAME VERSION INSTALLATION GUIDE
Spryker Core 202410.0 Install the Spryker Core feature
Spryker Core BO 202410.0 Install the Spryker Core Back Office feature
Marketplace Merchant 202410.0 Install the Marketplace Merchant feature
Acl 202410.0 Install the ACL feature

1) Install the required modules

Install the required modules using Composer:

composer require spryker-feature/marketplace-merchantportal-core:"202410.0" --update-with-dependencies
composer require spryker/security-merchant-portal-gui-extension
Verification

Make sure the following modules have been installed:

MODULE EXPECTED DIRECTORY
ZedUi vendor/spryker/zed-ui
GuiTable vendor/spryker/gui-table
AclMerchantPortal vendor/spryker/acl-merchant-portal
MerchantPortalApplication vendor/spryker/merchant-portal-application
MerchantUserPasswordResetMail vendor/spryker/merchant-user-password-reset-mail
Navigation vendor/spryker/navigation
SecurityMerchantPortalGui vendor/spryker/security-merchant-portal-gui
UserMerchantPortalGui vendor/spryker/user-merchant-portal-gui
UserMerchantPortalGuiExtension spryker/user-merchant-portal-gui-extension
SecurityMerchantPortalGuiExtension spryker/security-merchant-portal-gui-extension

2) Set up configuration

CONFIGURATION SPECIFICATION NAMESPACE
GuiTableConfig::getDefaultTimezone() Defines the default timezone for formatting the DateTime data to the ISO 8601 format. Pyz\Zed\GuiTable
AclMerchantPortalConfig::isMerchantToMerchantUserConjunctionByUsernameEnabled() Defines if the merchant to merchant user conjunction should be generated by using the users’ usernames or first and last names. Pyz\Zed\AclMerchantPortal
ZedNavigationConfig::getCacheFilePaths() Defines the absolute paths to cache files indexed by navigation types. Pyz\Zed\ZedNavigation
ZedNavigationConfig::getRootNavigationSchemaPaths() Defines the absolute paths to navigation schemas indexed by navigation types. Pyz\Zed\ZedNavigation
ZedNavigationConfig::getNavigationSchemaFileNamePatterns() Defines the navigation schema file name patters indexed by navigation types. Pyz\Zed\ZedNavigation

src/Pyz/Zed/GuiTable/GuiTableConfig.php

<?php

namespace Pyz\Zed\GuiTable;

use Spryker\Zed\GuiTable\GuiTableConfig as SprykerGuiTableConfig;

class GuiTableConfig extends SprykerGuiTableConfig
{
    /**
     * @return string|null
     */
    public function getDefaultTimezone(): ?string
    {
        return 'UTC';
    }
}

src/Pyz/Zed/AclMerchantPortal/AclMerchantPortalConfig.php

<?php

namespace Pyz\Zed\AclMerchantPortal;

use Spryker\Zed\AclMerchantPortal\AclMerchantPortalConfig as SprykerAclMerchantPortalConfig;

class AclMerchantPortalConfig extends SprykerAclMerchantPortalConfig
{
    /**
     * @return bool
     */
    public function isMerchantToMerchantUserConjunctionByUsernameEnabled(): bool
    {
        return true;
    }
}
src/Pyz/Zed/ZedNavigation/ZedNavigationConfig.php
<?php

namespace Spryker\Zed\ZedNavigation;

use Spryker\Zed\Kernel\AbstractBundleConfig;

class ZedNavigationConfig extends AbstractBundleConfig
{
    /**
     * @uses \Spryker\Zed\MerchantPortalApplication\Communication\Plugin\Twig\MerchantNavigationTypeTwigPlugin::NAVIGATION_TYPE_MAIN_MERCHANT_PORTAL
     *
     * @var string
     */
    protected const NAVIGATION_TYPE_MAIN_MERCHANT_PORTAL = 'main-merchant-portal';

    /**
     * @uses \Spryker\Zed\MerchantPortalApplication\Communication\Plugin\Twig\MerchantNavigationTypeTwigPlugin::NAVIGATION_TYPE_SECONDARY_MERCHANT_PORTAL
     *
     * @var string
     */
    protected const NAVIGATION_TYPE_SECONDARY_MERCHANT_PORTAL = 'secondary-merchant-portal';

    /**
     * @return array<string>
     */
    public function getCacheFilePaths(): array
    {
        $cacheFilePaths = parent::getCacheFilePaths();
        $cacheFilePaths[static::NAVIGATION_TYPE_MAIN_MERCHANT_PORTAL] = $this->getCacheDirName() . 'navigation-main-merchant-portal.cache';
        $cacheFilePaths[static::NAVIGATION_TYPE_SECONDARY_MERCHANT_PORTAL] = $this->getCacheDirName() . 'navigation-secondary-merchant-portal.cache';

        return $cacheFilePaths;
    }

    /**
     * @return array<string>
     */
    public function getRootNavigationSchemaPaths(): array
    {
        $rootNavigationSchemaPaths = parent::getRootNavigationSchemaPaths();
        $rootNavigationSchemaPaths[static::NAVIGATION_TYPE_MAIN_MERCHANT_PORTAL] = $this->getRootNavigationSchemasDirName() . 'navigation-main-merchant-portal.xml';
        $rootNavigationSchemaPaths[static::NAVIGATION_TYPE_SECONDARY_MERCHANT_PORTAL] = $this->getRootNavigationSchemasDirName() . 'navigation-secondary-merchant-portal.xml';

        return $rootNavigationSchemaPaths;
    }

    /**
     * @return array<string>
     */
    public function getNavigationSchemaFileNamePatterns(): array
    {
        $navigationSchemaFileNamePatterns = parent::getNavigationSchemaFileNamePatterns();
        $navigationSchemaFileNamePatterns[static::NAVIGATION_TYPE_MAIN_MERCHANT_PORTAL] = 'navigation-main-merchant-portal.xml';
        $navigationSchemaFileNamePatterns[static::NAVIGATION_TYPE_SECONDARY_MERCHANT_PORTAL] = 'navigation-secondary-merchant-portal.xml';

        return $navigationSchemaFileNamePatterns;
    }
}

3) Set up the database schema

  1. Set up the database schemas:

src/Pyz/Zed/Merchant/Persistence/Propel/Schema/spy_merchant.schema.xml

<?xml version="1.0"?>
<database xmlns="spryker:schema-01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="zed" xsi:schemaLocation="spryker:schema-01 https://static.spryker.com/schema-01.xsd" namespace="Orm\Zed\Merchant\Persistence" package="src.Orm.Zed.Merchant.Persistence">

    <table name="spy_merchant">
        <behavior name="event">
            <parameter name="spy_merchant-name" column="name"/>
            <parameter name="spy_merchant-is_active" column="is_active"/>
        </behavior>
        <behavior name="\Spryker\Zed\AclEntity\Persistence\Propel\Behavior\AclEntityBehavior"/>
    </table>

</database>

src/Pyz/Zed/MerchantUser/Persistence/Propel/Schema/spy_merchant_user.schema.xml

<?xml version="1.0"?>
<database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="zed" xsi:noNamespaceSchemaLocation="http://static.spryker.com/schema-01.xsd" namespace="Orm\Zed\MerchantUser\Persistence" package="src.Orm.Zed.MerchantUser.Persistence">

    <table name="spy_merchant_user">
        <behavior name="\Spryker\Zed\AclEntity\Persistence\Propel\Behavior\AclEntityBehavior"/>
    </table>

</database>
  1. Apply database changes and generate entity and transfer changes:
console transfer:generate
console propel:install
console transfer:generate

3) Optional: Set up configuration

  1. In src/Pyz/Zed/SecurityMerchantPortalGui/SecurityMerchantPortalGuiConfig.php, enable the blocking of recurring attempts to reset password by setting MERCHANT_PORTAL_SECURITY_BLOCKER_ENABLED to true;.

  2. In src/Pyz/Zed/SecurityMerchantPortalGui/SecurityMerchantPortalGuiConfig.php, add the following configuration:

namespace Pyz\Zed\SecurityMerchantPortalGui;

use Spryker\Zed\SecurityMerchantPortalGui\SecurityMerchantPortalGuiConfig as SprykerSecurityMerchantPortalGui;

class SecurityMerchantPortalGuiConfig extends SprykerSecurityMerchantPortalGui
{
    /**
     * @var bool
     */
    protected const MERCHANT_PORTAL_SECURITY_BLOCKER_ENABLED = true;
}

4) Set up behavior

Set up the following behaviors.

Integrate the following plugins

PLUGIN SPECIFICATION PREREQUISITES NAMESPACE
ZedMerchantUserSecurityPlugin Sets security firewalls (rules, handlers) for Marketplace users. Spryker\Zed\SecurityMerchantPortalGui\Communication\Plugin\Security
BooleanToStringTwigPlugin Adds a Twig function for converting Boolean to String. Spryker\Zed\ZedUi\Communication\Plugin\Twig
ZedUiNavigationTwigPlugin Adds a Twig function for rendering the navigation using web components. Spryker\Zed\ZedUi\Communication\Plugin
MerchantNavigationTypeTwigPlugin Adds mainMerchantNavigationType and secondaryMerchantNavigationType Twig global variables. Spryker\Zed\MerchantPortalApplication\Communication\Plugin\Twig
GuiTableApplicationPlugin Enables the GuiTable infrastructure for the Back Office. Spryker\Zed\GuiTable\Communication\Plugin\Application
GuiTableConfigurationTwigPlugin Adds a Twig function for rendering GuiTableConfiguration for the GuiTable web component. Spryker\Zed\GuiTable\Communication\Plugin\Twig
MerchantUserSecurityTokenUpdateMerchantUserPostChangePlugin Rewrites the Symfony security token for merchant users with MerchantUser and without IS_IMPERSONATOR roles granted. Spryker\Zed\SecurityMerchantPortalGui\Communication\Plugin\UserMerchantPortalGui
MerchantPortalConfigurationAclEntityMetadataConfigExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with event behavior composite data. Spryker\Zed\AclMerchantPortal\Communication\Plugin\AclEntity
MerchantAclEntitiesMerchantPostCreatePlugin Creates an ACL group, ACL role, ACL rules, ACL entity rules, and ACL entity segment for a provided merchant. Spryker\Zed\AclMerchantPortal\Communication\Plugin\Merchant
MerchantUserAclEntitiesMerchantUserPostCreatePlugin Creates an ACL group, ACL role, ACL rules, ACL entity rules, and ACL entity segment for a provided merchant user. Spryker\Zed\AclMerchantPortal\Communication\Plugin\MerchantUser
AclMerchantPortalMerchantUserRoleFilterPreConditionPlugin Checks if Symfony security authentication roles should be filtered out. Spryker\Zed\AclMerchantPortal\Communication\Plugin\MerchantUser
MerchantUserUserRoleFilterPlugin Filters ROLE_BACK_OFFICE_USER to prevent a merchant user from logging into the Back Office. Spryker\Zed\MerchantUser\Communication\Plugin\SecurityGui
ProductViewerForOfferCreationAclInstallerPlugin Provides ProductViewerForOfferCreation roles with rules and groups to create on installation. Spryker\Zed\AclMerchantPortal\Communication\Plugin\MerchantUser
AclGroupMerchantUserLoginRestrictionPlugin Checks if the merchant user login is restricted. Spryker\Zed\AclMerchantPortal\Communication\Plugin\SecurityMerchantPortalGui

src/Pyz/Zed/Twig/TwigDependencyProvider.php

<?php

namespace Pyz\Zed\Twig;

use Spryker\Zed\GuiTable\Communication\Plugin\Twig\GuiTableConfigurationTwigPlugin;
use Spryker\Zed\MerchantPortalApplication\Communication\Plugin\Twig\MerchantNavigationTypeTwigPlugin;
use Spryker\Zed\ZedUi\Communication\Plugin\Twig\BooleanToStringTwigPlugin;
use Spryker\Zed\ZedUi\Communication\Plugin\ZedUiNavigationTwigPlugin;
use Spryker\Zed\Twig\TwigDependencyProvider as SprykerTwigDependencyProvider;

class TwigDependencyProvider extends SprykerTwigDependencyProvider
{
    /**
     * @return array<\Spryker\Shared\TwigExtension\Dependency\Plugin\TwigPluginInterface>
     */
    protected function getTwigPlugins(): array
    {
        return [
            new ZedUiNavigationTwigPlugin(),
            new BooleanToStringTwigPlugin(),
            new GuiTableConfigurationTwigPlugin(),
            new MerchantNavigationTypeTwigPlugin(),
        ];
    }
}

src/Pyz/Zed/Application/ApplicationDependencyProvider.php

<?php

namespace Pyz\Zed\Application;

use Spryker\Zed\Application\ApplicationDependencyProvider as SprykerApplicationDependencyProvider;
use Spryker\Zed\GuiTable\Communication\Plugin\Application\GuiTableApplicationPlugin;

class ApplicationDependencyProvider extends SprykerApplicationDependencyProvider
{

    /**
     * @return array<\Spryker\Shared\ApplicationExtension\Dependency\Plugin\ApplicationPluginInterface>
     */
    protected function getApplicationPlugins(): array
    {
        return [  
            new GuiTableApplicationPlugin(),
        ];
    }
}

src/Pyz/Zed/Security/SecurityDependencyProvider.php

<?php

namespace Pyz\Zed\Security;

use Spryker\Zed\Security\SecurityDependencyProvider as SprykerSecurityDependencyProvider;
use Spryker\Zed\SecurityMerchantPortalGui\Communication\Plugin\Security\ZedMerchantUserSecurityPlugin;

class SecurityDependencyProvider extends SprykerSecurityDependencyProvider
{
    /**
     * @return array<\Spryker\Shared\SecurityExtension\Dependency\Plugin\SecurityPluginInterface>
     */
    protected function getSecurityPlugins(): array
    {
        return [
            new ZedMerchantUserSecurityPlugin(),
        ];
    }
}

src/Pyz/Zed/SecurityGui/SecurityGuiDependencyProvider.php

<?php

namespace Pyz\Zed\SecurityGui;

use Spryker\Zed\MerchantUser\Communication\Plugin\SecurityGui\MerchantUserUserRoleFilterPlugin;
use Spryker\Zed\SecurityGui\SecurityGuiDependencyProvider as SprykerSecurityGuiDependencyProvider;

class SecurityGuiDependencyProvider extends SprykerSecurityGuiDependencyProvider
{
    /**
     * @return array<\Spryker\Zed\SecurityGuiExtension\Dependency\Plugin\UserRoleFilterPluginInterface>
     */
    protected function getUserRoleFilterPlugins(): array
    {
        return [
            new MerchantUserUserRoleFilterPlugin(),
        ];
    }
}
Verification

Make sure that merchant users and users whose ACL group doesn’t have a Back Office allowed ACL group reference can’t log into the Back Office.

src/Pyz/Zed/UserMerchantPortalGui/UserMerchantPortalGuiDependencyProvider.php

<?php

namespace Pyz\Zed\UserMerchantPortalGui;

use Spryker\Zed\SecurityMerchantPortalGui\Communication\Plugin\UserMerchantPortalGui\MerchantUserSecurityTokenUpdateMerchantUserPostChangePlugin;
use Spryker\Zed\UserMerchantPortalGui\UserMerchantPortalGuiDependencyProvider as SprykerUserMerchantPortalGuiDependencyProvider;

class UserMerchantPortalGuiDependencyProvider extends SprykerUserMerchantPortalGuiDependencyProvider
{
    /**
     * @return array<\Spryker\Zed\UserMerchantPortalGuiExtension\Dependency\Plugin\MerchantUserPostChangePluginInterface>
     */
    public function getMerchantUserPostChangePlugins(): array
    {
        return [
            new MerchantUserSecurityTokenUpdateMerchantUserPostChangePlugin(),
        ];
    }
}

src/Pyz/Zed/AclEntity/AclEntityDependencyProvider.php

<?php

namespace Pyz\Zed\AclEntity;

use Spryker\Zed\AclEntity\AclEntityDependencyProvider as SprykerAclEntityDependencyProvider;
use Spryker\Zed\AclMerchantPortal\Communication\Plugin\AclEntity\MerchantPortalConfigurationAclEntityMetadataConfigExpanderPlugin;

class AclEntityDependencyProvider extends SprykerAclEntityDependencyProvider
{
    /**
     * @return array<\Spryker\Zed\AclEntityExtension\Dependency\Plugin\AclEntityMetadataConfigExpanderPluginInterface>
     */
    protected function getAclEntityMetadataCollectionExpanderPlugins(): array
    {
        return [
            new MerchantPortalConfigurationAclEntityMetadataConfigExpanderPlugin(),
        ];
    }
}

src/Pyz/Zed/Merchant/MerchantDependencyProvider.php

<?php

namespace Pyz\Zed\Merchant;

use Spryker\Zed\AclMerchantPortal\Communication\Plugin\Merchant\MerchantAclEntitiesMerchantPostCreatePlugin;
use Spryker\Zed\Merchant\MerchantDependencyProvider as SprykerMerchantDependencyProvider;

class MerchantDependencyProvider extends SprykerMerchantDependencyProvider
{
    /**
     * @return array<\Spryker\Zed\MerchantExtension\Dependency\Plugin\MerchantPostCreatePluginInterface>
     */
    protected function getMerchantPostCreatePlugins(): array
    {
        return [
            new MerchantAclEntitiesMerchantPostCreatePlugin(),
        ];
    }
}

src/Pyz/Zed/MerchantUser/MerchantUserDependencyProvider.php

<?php

namespace Pyz\Zed\MerchantUser;

use Spryker\Zed\AclMerchantPortal\Communication\Plugin\MerchantUser\AclMerchantPortalMerchantUserRoleFilterPreConditionPlugin;
use Spryker\Zed\AclMerchantPortal\Communication\Plugin\MerchantUser\MerchantUserAclEntitiesMerchantUserPostCreatePlugin;
use Spryker\Zed\MerchantUser\MerchantUserDependencyProvider as SprykerMerchantUserDependencyProvider;

class MerchantUserDependencyProvider extends SprykerMerchantUserDependencyProvider
{
    /**
     * @return array<\Spryker\Zed\MerchantUserExtension\Dependency\Plugin\MerchantUserPostCreatePluginInterface>
     */
    protected function getMerchantUserPostCreatePlugins(): array
    {
        return [
            new MerchantUserAclEntitiesMerchantUserPostCreatePlugin(),
        ];
    }

    /**
     * @return array<\Spryker\Zed\MerchantUserExtension\Dependency\Plugin\MerchantUserRoleFilterPreConditionPluginInterface>
     */
    protected function getMerchantUserRoleFilterPreConditionPlugins(): array
    {
        return [
            new AclMerchantPortalMerchantUserRoleFilterPreConditionPlugin(),
        ];
    }
}
Verification

Make sure that non-merchant users whose ACL group has a Back Office allowed ACL group reference can log into the Back Office. You can check the reference in AclMerchantPortalConfig::getBackofficeAllowedAclGroupReferences().

src/Pyz/Zed/Acl/AclDependencyProvider.php

<?php

namespace Pyz\Zed\Acl;

use Spryker\Zed\Acl\AclDependencyProvider as SprykerAclDependencyProvider;
use Spryker\Zed\AclMerchantPortal\Communication\Plugin\MerchantUser\ProductViewerForOfferCreationAclInstallerPlugin;

class AclDependencyProvider extends SprykerAclDependencyProvider
{
    /**
     * @return array<\Spryker\Zed\AclExtension\Dependency\Plugin\AclInstallerPluginInterface>
     */
    protected function getAclInstallerPlugins(): array
    {
        return [
            new ProductViewerForOfferCreationAclInstallerPlugin(),
        ];
    }
}

src/Pyz/Zed/SecurityMerchantPortalGui/SecurityMerchantPortalGuiDependencyProvider.php

<?php

namespace Pyz\Zed\SecurityMerchantPortalGui;

use Spryker\Zed\AclMerchantPortal\Communication\Plugin\SecurityMerchantPortalGui\AclGroupMerchantUserLoginRestrictionPlugin;
use Spryker\Zed\SecurityMerchantPortalGui\SecurityMerchantPortalGuiDependencyProvider as SprykerSecurityMerchantPortalGuiDependencyProvider;

class SecurityMerchantPortalGuiDependencyProvider extends SprykerSecurityMerchantPortalGuiDependencyProvider
{
    /**
     * @return array<\Spryker\Zed\SecurityMerchantPortalGuiExtension\Dependency\Plugin\MerchantUserLoginRestrictionPluginInterface>
     */
    protected function getMerchantUserLoginRestrictionPlugins(): array
    {
        return [
             new AclGroupMerchantUserLoginRestrictionPlugin(),
        ];
    }
}

Enable Merchant Portal infrastructural plugins

  1. Enable the following infrastructural plugins:
PLUGIN SPECIFICATION PREREQUISITES NAMESPACE
SessionApplicationPlugin Registers a session in Application. Spryker\Zed\Session\Communication\Plugin\Application
TwigApplicationPlugin Registers Twig in Application. Spryker\Zed\Twig\Communication\Plugin\Application
EventDispatcherApplicationPlugin Extends EventDispatcher with EventDispatcherExtensionPlugins. Spryker\Zed\EventDispatcher\Communication\Plugin\Application
LocaleApplicationPlugin Adds the Locale service. Spryker\Zed\Locale\Communication\Plugin\Application
TranslatorApplicationPlugin Adds the Translator service. Spryker\Zed\Translator\Communication\Plugin\Application
MessengerApplicationPlugin Adds the Messenger service to the Container. Spryker\Zed\Messenger\Communication\Plugin\Application
PropelApplicationPlugin Initializes PropelOrm to be used in Zed. Spryker\Zed\Propel\Communication\Plugin\Application
MerchantPortalRouterApplicationPlugin Adds the Router service. Spryker\Zed\Router\Communication\Plugin\Application
HttpApplicationPlugin Sets the trusted proxies and host. Sets the cookies service identifier. Adds HttpKernel, RequestStack, and RequestContext to the container. Spryker\Zed\Http\Communication\Plugin\Application
ErrorHandlerApplicationPlugin Register the Whoops error handler, which provides a pretty error interface when enabled. Spryker\Zed\ErrorHandler\Communication\Plugin\Application
FormApplicationPlugin Adds the form.factory service, form.csrf_provider service, and the global FORM_FACTORY service as an alias for form.factory. Spryker\Zed\Form\Communication\Plugin\Application
ValidatorApplicationPlugin Adds the validator service. Spryker\Zed\Validator\Communication\Plugin\Application
GuiTableApplicationPlugin Enables the GuiTable infrastructure for Zed. Spryker\Zed\GuiTable\Communication\Plugin\Application
ZedSecurityApplicationPlugin Adds security applications to Application. Spryker\Zed\Security\Communication\Plugin\Application
ZedUiApplicationPlugin Adds the SERVICE_ZED_UI_FACTORY service. Spryker\Zed\ZedUi\Communication\Plugin\Application
AclEntityApplicationPlugin Enables ACL for the whole Application. Spryker\Zed\AclEntity\Communication\Plugin\Application
src/Pyz/Zed/MerchantPortalApplication/MerchantPortalApplicationDependencyProvider.php
<?php

namespace Pyz\Zed\MerchantPortalApplication;

use Spryker\Zed\AclEntity\Communication\Plugin\Application\AclEntityApplicationPlugin;
use Spryker\Zed\ErrorHandler\Communication\Plugin\Application\ErrorHandlerApplicationPlugin;
use Spryker\Zed\EventDispatcher\Communication\Plugin\Application\EventDispatcherApplicationPlugin;
use Spryker\Zed\Form\Communication\Plugin\Application\FormApplicationPlugin;
use Spryker\Zed\GuiTable\Communication\Plugin\Application\GuiTableApplicationPlugin;
use Spryker\Zed\Http\Communication\Plugin\Application\HttpApplicationPlugin;
use Spryker\Zed\Locale\Communication\Plugin\Application\LocaleApplicationPlugin;
use Spryker\Zed\MerchantPortalApplication\MerchantPortalApplicationDependencyProvider as SprykerMerchantPortalApplicationDependencyProvider;
use Spryker\Zed\Messenger\Communication\Plugin\Application\MessengerApplicationPlugin;
use Spryker\Zed\Propel\Communication\Plugin\Application\PropelApplicationPlugin;
use Spryker\Zed\Router\Communication\Plugin\Application\MerchantPortalRouterApplicationPlugin;
use Spryker\Zed\Security\Communication\Plugin\Application\ZedSecurityApplicationPlugin;
use Spryker\Zed\Session\Communication\Plugin\Application\SessionApplicationPlugin;
use Spryker\Zed\Translator\Communication\Plugin\Application\TranslatorApplicationPlugin;
use Spryker\Zed\Twig\Communication\Plugin\Application\TwigApplicationPlugin;
use Spryker\Zed\Validator\Communication\Plugin\Application\ValidatorApplicationPlugin;
use Spryker\Zed\ZedUi\Communication\Plugin\Application\ZedUiApplicationPlugin;

class MerchantPortalApplicationDependencyProvider extends SprykerMerchantPortalApplicationDependencyProvider
{
    /**
     * @return array<\Spryker\Shared\ApplicationExtension\Dependency\Plugin\ApplicationPluginInterface>
     */
    protected function getMerchantPortalApplicationPlugins(): array
    {
        return [
            new SessionApplicationPlugin(),
            new TwigApplicationPlugin(),
            new EventDispatcherApplicationPlugin(),
            new LocaleApplicationPlugin(),
            new TranslatorApplicationPlugin(),
            new MessengerApplicationPlugin(),
            new PropelApplicationPlugin(),
            new MerchantPortalRouterApplicationPlugin(),
            new HttpApplicationPlugin(),
            new ErrorHandlerApplicationPlugin(),
            new FormApplicationPlugin(),
            new ValidatorApplicationPlugin(),
            new GuiTableApplicationPlugin(),
            new ZedSecurityApplicationPlugin(),
            new ZedUiApplicationPlugin(),
            new AclEntityApplicationPlugin(),
        ];
    }
}

src/Pyz/Zed/MerchantPortalApplication/Communication/Bootstrap/MerchantPortalBootstrap.php

<?php

namespace Pyz\Zed\MerchantPortalApplication\Communication\Bootstrap;

use Spryker\Zed\MerchantPortalApplication\Communication\Bootstrap\MerchantPortalBootstrap as MerchantPortalApplicationBootstrap;

class MerchantPortalBootstrap extends MerchantPortalApplicationBootstrap
{
}

src/Pyz/Zed/MerchantPortalApplication/Communication/MerchantPortalApplicationCommunicationFactory.php

<?php

namespace Pyz\Zed\MerchantPortalApplication\Communication;

use Spryker\Zed\MerchantPortalApplication\Communication\MerchantPortalApplicationCommunicationFactory as SprykerMerchantPortalApplicationCommunicationFactory;

class MerchantPortalApplicationCommunicationFactory extends SprykerMerchantPortalApplicationCommunicationFactory
{
}

src/Pyz/Zed/Router/RouterDependencyProvider.php

<?php

namespace Pyz\Zed\Router;

use Spryker\Zed\Router\Communication\Plugin\Router\MerchantPortalRouterPlugin;
use Spryker\Zed\Router\RouterDependencyProvider as SprykerRouterDependencyProvider;

class RouterDependencyProvider extends SprykerRouterDependencyProvider
{
    /**
     * @return array|array<\Spryker\Zed\RouterExtension\Dependency\Plugin\RouterPluginInterface>
     */
    protected function getMerchantPortalRouterPlugins(): array
    {
        return [
            new MerchantPortalRouterPlugin()
        ];
    }
}
  1. Open access to the Merchant Portal login page by default:

config/Shared/config_default.php

<?php

$config[AclConstants::ACL_DEFAULT_RULES][] = [
    [
        'bundle' => 'security-merchant-portal-gui',
        'controller' => 'login',
        'action' => 'index',
        'type' => 'allow',
    ],
];
  1. Add a console command for warming up the Merchant Portal router cache:

src/Pyz/Zed/Console/ConsoleDependencyProvider.php

<?php

namespace Pyz\Zed\Console;

use Spryker\Zed\Console\ConsoleDependencyProvider as SprykerConsoleDependencyProvider;
use Spryker\Zed\Router\Communication\Plugin\Console\MerchantPortalRouterCacheWarmUpConsole;

class ConsoleDependencyProvider extends SprykerConsoleDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return array<\Symfony\Component\Console\Command\Command>
     */
    protected function getConsoleCommands(Container $container): array
    {
        $commands = [
            new MerchantPortalRouterCacheWarmUpConsole(),
        ];

        return $commands;
    }
}

config/install/docker.yml

env:
    NEW_RELIC_ENABLED: 0

sections:
    build:
        router-cache-warmup-merchant-portal:
            command: 'vendor/bin/console router:cache:warm-up:merchant-portal'

5) Set up transfer objects

Generate transfer objects:

console transfer:generate
Verification

Make sure the following changes have been applied in transfer objects:

TRANSFER TYPE EVENT PATH
GuiTableDataRequest class Created src/Generated/Shared/Transfer/GuiTableDataRequestTransfer
GuiTableConfiguration class Created src/Generated/Shared/Transfer/GuiTableConfigurationTransfer
GuiTableColumnConfiguration class Created src/Generated/Shared/Transfer/GuiTableColumnConfigurationTransfer
GuiTableTitleConfiguration class Created src/Generated/Shared/Transfer/GuiTableTitleConfigurationTransfer
GuiTableDataSourceConfiguration class Created src/Generated/Shared/Transfer/GuiTableDataSourceConfigurationTransfer
GuiTableRowActionsConfiguration class Created src/Generated/Shared/Transfer/GuiTableRowActionsConfigurationTransfer
GuiTableBatchActionsConfiguration class Created src/Generated/Shared/Transfer/GuiTableBatchActionsConfigurationTransfer
GuiTablePaginationConfiguration class Created src/Generated/Shared/Transfer/GuiTablePaginationConfigurationTransfer
GuiTableSearchConfiguration class Created src/Generated/Shared/Transfer/GuiTableSearchConfigurationTransfer
GuiTableFiltersConfiguration class Created src/Generated/Shared/Transfer/GuiTableFiltersConfigurationTransfer
GuiTableItemSelectionConfiguration class Created src/Generated/Shared/Transfer/GuiTableItemSelectionConfigurationTransfer
GuiTableSyncStateUrlConfiguration class Created src/Generated/Shared/Transfer/GuiTableSyncStateUrlConfigurationTransfer
GuiTableEditableConfiguration class Created src/Generated/Shared/Transfer/GuiTableEditableConfigurationTransfer
GuiTableEditableCreateConfiguration class Created src/Generated/Shared/Transfer/GuiTableEditableCreateConfigurationTransfer
GuiTableEditableUpdateConfiguration class Created src/Generated/Shared/Transfer/GuiTableEditableUpdateConfigurationTransfer
GuiTableEditableButton class Created src/Generated/Shared/Transfer/GuiTableEditableButtonTransfer
GuiTableEditableUrl class Created src/Generated/Shared/Transfer/GuiTableEditableUrlTransfer
GuiTableEditableInitialData class Created src/Generated/Shared/Transfer/GuiTableEditableInitialDataTransfer
GuiTableEditableDataError class Created src/Generated/Shared/Transfer/GuiTableEditableDataErrorTransfer
GuiTableDataResponse class Created src/Generated/Shared/Transfer/GuiTableDataResponseTransfer
GuiTableRowDataResponse class Created src/Generated/Shared/Transfer/GuiTableRowDataResponseTransfer
GuiTableDataResponsePayload class Created src/Generated/Shared/Transfer/GuiTableDataResponsePayloadTransfer
SelectGuiTableFilterTypeOptions class Created src/Generated/Shared/Transfer/SelectGuiTableFilterTypeOptionsTransfer
OptionSelectGuiTableFilterTypeOptions class Created src/Generated/Shared/Transfer/OptionSelectGuiTableFilterTypeOptionsTransfer
GuiTableFilter class Created src/Generated/Shared/Transfer/GuiTableFilterTransfer
GuiTableRowAction class Created src/Generated/Shared/Transfer/GuiTableRowActionTransfer
GuiTableRowActionOptions class Created src/Generated/Shared/Transfer/GuiTableRowActionOptionsTransfer
DateRangeGuiTableFilterTypeOptions class Created src/Generated/Shared/Transfer/DateRangeGuiTableFilterTypeOptionsTransfer
CriteriaRangeFilter class Created src/Generated/Shared/Transfer/CriteriaRangeFilterTransfer
GuiTableBatchAction class Created src/Generated/Shared/Transfer/GuiTableBatchActionTransfer
GuiTableBatchActionOptions class Created src/Generated/Shared/Transfer/GuiTableBatchActionOptionsTransfer
GuiTableColumnConfiguratorConfiguration class Created src/Generated/Shared/Transfer/GuiTableColumnConfiguratorConfigurationTransfer
ZedUiFormResponseAction class Created src/Generated/Shared/Transfer/ZedUiFormResponseActionTransfer
Group class Created src/Generated/Shared/Transfer/GroupTransfer
Groups class Created src/Generated/Shared/Transfer/GroupsTransfer
Role class Created src/Generated/Shared/Transfer/RoleTransfer
Roles class Created src/Generated/Shared/Transfer/RolesTransfer
Rule class Created src/Generated/Shared/Transfer/RuleTransfer
Rules class Created src/Generated/Shared/Transfer/RulesTransfer
User class Created src/Generated/Shared/Transfer/UserTransfer
AclRoleCriteria class Created src/Generated/Shared/Transfer/AclRoleCriteriaTransfer
GroupCriteria class Created src/Generated/Shared/Transfer/GroupCriteriaTransfer
NavigationItem class Created src/Generated/Shared/Transfer/NavigationItemTransfer
NavigationItemCollection class Created src/Generated/Shared/Transfer/NavigationItemCollectionTransfer
AclEntityRule class Created src/Generated/Shared/Transfer/AclEntityRuleTransfer
UserCollection class Created src/Generated/Shared/Transfer/UserCollectionTransfer
UserConditions class Created src/Generated/Shared/Transfer/UserConditionsTransfer
UserCriteria class Created src/Generated/Shared/Transfer/UserCriteriaTransfer
AclEntityMetadataConfig class Created src/Generated/Shared/Transfer/AclEntityMetadataConfigTransfer
AclEntitySegment class Created src/Generated/Shared/Transfer/AclEntitySegmentTransfer
AclEntitySegmentRequest class Created src/Generated/Shared/Transfer/AclEntitySegmentRequestTransfer
AclEntityRuleRequest class Created src/Generated/Shared/Transfer/AclEntityRuleRequestTransfer
AclEntityRule class Created src/Generated/Shared/Transfer/AclEntityRuleTransfer
AclEntityRuleCollection class Created src/Generated/Shared/Transfer/AclEntityRuleCollectionTransfer
AclEntitySegmentResponse class Created src/Generated/Shared/Transfer/AclEntitySegmentResponseTransfer
AclEntitySegmentCriteria class Created src/Generated/Shared/Transfer/AclEntitySegmentCriteriaTransfer
AclEntityRuleCriteria class Created src/Generated/Shared/Transfer/AclEntityRuleCriteriaTransfer
AclEntityRuleResponse class Created src/Generated/Shared/Transfer/AclEntityRuleResponseTransfer
AclEntityMetadata class Created src/Generated/Shared/Transfer/AclEntityMetadataTransfer
AclEntityParentMetadata class Created src/Generated/Shared/Transfer/AclEntityParentMetadataTransfer
AclEntityParentConnectionMetadata class Created src/Generated/Shared/Transfer/AclEntityParentConnectionMetadataTransfer
AclEntityMetadataCollection class Created src/Generated/Shared/Transfer/AclEntityMetadataCollectionTransfer
MerchantResponse class Created src/Generated/Shared/Transfer/MerchantResponseTransfer
Merchant class Created src/Generated/Shared/Transfer/MerchantTransfer
MerchantError class Created src/Generated/Shared/Transfer/MerchantErrorTransfer
MerchantUser class Created src/Generated/Shared/Transfer/MerchantUserTransfer
MerchantUserCriteria class Created src/Generated/Shared/Transfer/MerchantUserCriteriaTransfer
MerchantUserResponse class Created src/Generated/Shared/Transfer/MerchantUserResponseTransfer
Message class Created src/Generated/Shared/Transfer/MessageTransfer
MerchantCriteria class Created src/Generated/Shared/Transfer/MerchantCriteriaTransfer
UserPasswordResetRequest class Created src/Generated/Shared/Transfer/UserPasswordResetRequestTransfer
Mail class Created src/Generated/Shared/Transfer/MailTransfer
MailRecipient class Created src/Generated/Shared/Transfer/MailRecipientTransfer
MailTemplate class Created src/Generated/Shared/Transfer/MailTemplateTransfer
MailSender class Created src/Generated/Shared/Transfer/MailSenderTransfer
Navigation class Created src/Generated/Shared/Transfer/NavigationTransfer
NavigationCriteria class Created src/Generated/Shared/Transfer/NavigationCriteriaTransfer
DuplicateNavigation class Created src/Generated/Shared/Transfer/DuplicateNavigationTransfer
NavigationResponse class Created src/Generated/Shared/Transfer/NavigationResponseTransfer
NavigationError class Created src/Generated/Shared/Transfer/NavigationErrorTransfer
NavigationNode class Created src/Generated/Shared/Transfer/NavigationNodeTransfer
NavigationNodeLocalizedAttributes class Created src/Generated/Shared/Transfer/NavigationNodeLocalizedAttributesTransfer
NavigationTree class Created src/Generated/Shared/Transfer/NavigationTreeTransfer
NavigationTreeNode class Created src/Generated/Shared/Transfer/NavigationTreeNodeTransfer
Url class Created src/Generated/Shared/Transfer/UrlTransfer
Locale class Created src/Generated/Shared/Transfer/LocaleTransfer
LocaleCriteria class Created src/Generated/Shared/Transfer/LocaleCriteriaTransfer
LocaleConditions class Created src/Generated/Shared/Transfer/LocaleConditionsTransfer
SecurityBlockerConfigurationSettings class Created src/Generated/Shared/Transfer/SecurityBlockerConfigurationSettingsTransfer
ZedUiFormResponse class Created src/Generated/Shared/Transfer/ZedUiFormResponseTransfer

Install feature frontend

Follow the steps below to install the Merchant Portal Core feature frontend.

Prerequisites

Environment requirements:

  • Node.js version 18 or higher.
  • npm version 9 or higher.

Application requirements: if you want to integrate the builder, update the following packages.

NAME VERSION
Discount >= 9.7.4
Gui >= 3.30.2
Product Relation >= 2.4.3

1) Install the required modules

composer require spryker/dashboard-merchant-portal-gui:"^2.1.0" --update-with-dependencies
Verification

Make sure the following modules have been installed:

MODULE EXPECTED DIRECTORY
DashboardMerchantPortalGui vendor/spryker/dashboard-merchant-portal-gui
DashboardMerchantPortalGuiExtension vendor/spryker/dashboard-merchant-portal-gui-extension

2) Set up transfer objects

Generate transfer changes:

console transfer:generate
Verification

Make sure the following changes have been applied in transfer objects:

TRANSFER TYPE EVENT PATH
MerchantDashboardCard object Created src/Generated/Shared/Transfer/MerchantDashboardCardTransfer
MerchantDashboardActionButton object Created src/Generated/Shared/Transfer/MerchantDashboardActionButtonTransfer

3) Build the navigation cache

console navigation:build-cache
Verification

Make sure the Merchant Portal navigation has the Dashboard menu.

4) Set up marketplace builder configs

  1. Add the following files to the root folder:
wget -O angular.json https://raw.githubusercontent.com/spryker-shop/suite/master/angular.json
wget -O nx.json https://raw.githubusercontent.com/spryker-shop/suite/master/nx.json
wget -O .browserslistrc https://raw.githubusercontent.com/spryker-shop/suite/master/.browserslistrc
wget -O .npmrc https://raw.githubusercontent.com/spryker-shop/suite/master/.npmrc
wget -O .stylelintrc.mp.js https://raw.githubusercontent.com/spryker-shop/suite/master/.stylelintrc.mp.js
  1. Rename the default tsconfig.json to tsconfig.base.json and create tsconfig.yves.json and tsconfig.mp.json:
mv tsconfig.json tsconfig.base.json
wget -O tsconfig.yves.json https://raw.githubusercontent.com/spryker-shop/suite/master/tsconfig.yves.json
wget -O tsconfig.mp.json https://raw.githubusercontent.com/spryker-shop/suite/master/tsconfig.mp.json
wget -O tsconfig.json https://raw.githubusercontent.com/spryker-shop/suite/master/tsconfig.json
  1. In tslint.json, add the following paths to exclude:
  • src/Pyz/Zed/*/Presentation/Components/**
  • vendor/**
  • **/node_modules/**
  1. Add the .eslintrc.mp.json file:
wget -O .eslintrc.mp.json https://raw.githubusercontent.com/spryker-shop/suite/master/.eslintrc.mp.json
  1. Install npm dependencies:
npm i @angular/animations@~15.0.3 @angular/cdk@~15.0.3 @angular/common@~15.0.3 @angular/compiler@~15.0.3 @angular/core@~15.0.3 @angular/elements@~15.0.3 @angular/forms@~15.0.3 @angular/platform-browser@~15.0.3 @angular/platform-browser-dynamic@~15.0.3 @angular/router@~15.0.3 rxjs@~7.5.7 zone.js@~0.12.0
  1. Install npm dev dependencies:
npm i -D @angular-builders/custom-webpack@~15.0.0 @angular-devkit/build-angular@~15.0.3 @angular-eslint/builder@~15.0.0 @angular-eslint/eslint-plugin@~15.0.0 @angular-eslint/eslint-plugin-template@~15.0.0 @angular-eslint/schematics@~15.0.0 @angular-eslint/template-parser@~15.0.0 @angular/cli@~15.0.3 @angular/compiler-cli@~15.0.3 @angular/language-service@~15.0.3 @babel/plugin-proposal-class-properties@~7.17.12 @babel/plugin-transform-runtime@~7.17.12 @babel/preset-typescript@~7.17.12 @jsdevtools/file-path-filter@~3.0.2 @nrwl/cli@~15.0.7 @nrwl/jest@~15.0.7 @nrwl/workspace@~15.0.7 @spryker/oryx-for-zed@~3.0.0 @types/jest@~28.1.1 @types/node@~14.14.33 @types/webpack@~5.28.0 @typescript-eslint/eslint-plugin@~5.44.0 @@typescript-eslint/parser@~5.44.0 eslint@~8.28.0 eslint-plugin-deprecation@~1.3.3 jest@~28.1.3 jest-environment-jsdom@~28.1.1 jest-preset-angular@~12.2.3 nx@~15.0.7 postcss@~8.4.20 npm-run-all@~4.1.5 rimraf@~3.0.2 ts-jest@~28.0.8 ts-node@~10.9.1 tslib@~2.0.0 typescript@~4.8.4 webpack@~5.74.0 webpack-merge@~5.8.0
  1. Update package.json:

package.json

{
    "workspaces": [
        "vendor/spryker/*",
        "vendor/spryker/*/assets/Zed"
    ],
    "scripts": {
        "mp:build": "ng build",
        "mp:build:watch": "ng build --watch",
        "mp:build:production": "ng build --configuration production",
        "mp:stylelint": "node ./frontend/merchant-portal/stylelint",
        "mp:lint": "ng lint",
        "mp:test": "ng test",
        "mp:clean": "run-s mp:clean:*",
        "mp:clean:dist": "rimraf public/MerchantPortal/assets/js",
        "mp:update:paths": "node ./frontend/merchant-portal/update-config-paths",
        "postinstall": "npm run mp:update:paths"
    },
    "engines": {
        "node": ">=18.0.0",
        "npm": ">=9.0.0"
    }
}
  1. For Yves in frontend/settings.js, update the globalSettings.paths object to point to the updated tsconfig:

frontend/settings.js

const globalSettings = {
    ...
    paths: {
        tsConfig: './tsconfig.yves.json',
        ...
    }
};
  1. From the project’s root, install Angular CLI:
npm i -g @angular/cli@15.0.3
Verification

Run ng --version and make sure the version is 15.0.3.

  1. Install project dependencies:
npm install
Troubleshooting
  • Error: Missing write access to node_modules/mp-profile
  • Solution: Delete the node_modules/mp-profile file and create a folder with the same name.
Verification

Check if the marketplace packages are located in the node_modules/@spryker folder—for example, utils.

5) Install the marketplace builder

Add the merchant-portal folder and builder files:

mkdir frontend/merchant-portal
wget -O frontend/merchant-portal/entry-points.js https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/entry-points.js
wget -O frontend/merchant-portal/html-transform.js https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/html-transform.js
wget -O frontend/merchant-portal/jest.config.ts https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/jest.config.ts
wget -O frontend/merchant-portal/jest.preset.js https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/jest.preset.js
wget -O frontend/merchant-portal/mp-paths.js https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/mp-paths.js
wget -O frontend/merchant-portal/stylelint.js https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/stylelint.js
wget -O frontend/merchant-portal/test-setup.ts https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/test-setup.ts
wget -O frontend/merchant-portal/tsconfig.spec.json https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/tsconfig.spec.json
wget -O frontend/merchant-portal/update-config-paths.js https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/update-config-paths.js
wget -O frontend/merchant-portal/utils.js https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/utils.js
wget -O frontend/merchant-portal/webpack.config.ts https://raw.githubusercontent.com/spryker-shop/suite/master/frontend/merchant-portal/webpack.config.ts

6) Add files for the Merchant Portal entry point

public/MerchantPortal/index.php

<?php

use Pyz\Zed\MerchantPortalApplication\Communication\Bootstrap\MerchantPortalBootstrap;
use Spryker\Shared\Config\Application\Environment;
use Spryker\Shared\ErrorHandler\ErrorHandlerEnvironment;

define('APPLICATION', 'MERCHANT_PORTAL');
defined('APPLICATION_ROOT_DIR') || define('APPLICATION_ROOT_DIR', dirname(__DIR__, 2));

require_once APPLICATION_ROOT_DIR . '/vendor/autoload.php';

Environment::initialize();

$errorHandlerEnvironment = new ErrorHandlerEnvironment();
$errorHandlerEnvironment->initialize();

$bootstrap = new MerchantPortalBootstrap();
$bootstrap
    ->boot()
    ->run();

public/MerchantPortal/maintenance/index.html

<!DOCTYPE html>
<html lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Spryker Merchant Portal - Maintenance</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta name="description" content="" />
        <meta name="keywords" content="" />
        <link href="http://fonts.googleapis.com/css?family=PT+Mono" rel="stylesheet" type="text/css" />
    </head>
    <style>
        body {
            font-family: 'PT Mono', sans-serif;
        }
        #so-doc {
            margin: 0 auto;
            width: 960px;
        }
    </style>
    <body>
        <div id="so-doc">
            <div>
                <pre>
                PAGE UNDER CONSTRUCTION!

                Come back in a few minutes...
                </pre>
            </div>
        </div>
    </body>
</html>

public/MerchantPortal/maintenance/maintenance.php

<?php

/**
 * Copyright © 2017-present Spryker Systems GmbH. All rights reserved.
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
 */

if (file_exists(__DIR__ . '/maintenance.marker')) {
    http_response_code(503);
    echo file_get_contents(__DIR__ . '/index.html');
    exit(1);
}

src/Pyz/Zed/ZedUi/Presentation/Components/app/app.module.ts

import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { DefaultMerchantPortalConfigModule, RootMerchantPortalModule } from '@mp/zed-ui';
import { DefaultTableConfigModule } from '@mp/gui-table';

@NgModule({
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        HttpClientModule,
        RootMerchantPortalModule,
        DefaultMerchantPortalConfigModule,
        DefaultTableConfigModule,
    ],
})
export class AppModule extends RootMerchantPortalModule {}

src/Pyz/Zed/ZedUi/Presentation/Components/environments/environment.prod.ts

export const environment = {
    production: true,
};

src/Pyz/Zed/ZedUi/Presentation/Components/environments/environment.ts

export const environment = {
    production: false,
};

src/Pyz/Zed/ZedUi/Presentation/Components/index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>ZedUi</title>
        <base href="/" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    </head>
    <body></body>
</html>

src/Pyz/Zed/ZedUi/Presentation/Components/main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((error) => console.error(error));

src/Pyz/Zed/ZedUi/Presentation/Components/polyfills.ts

import '@mp/polyfills';
Verification

Build the Merchant Portal:

npm run mp:build

If the regular build doesn’t work, try running a full rebuild:

rm -rf node_modules && npm cache clean --force && npm install && npm run mp:build

6) Adjust deployment configs

To configure the deployment configuration to automatically install and build the Merchant Portal, change frontend dependencies and installation commands in the needed deployment file:

  1. Remove Yves dependency installation commands: dependencies-install and yves-isntall-dependencies.
  2. Add the following console commands:

src/Pyz/Zed/Console/ConsoleDependencyProvider.php

<?php

namespace Pyz\Zed\Console;

use Spryker\Zed\Console\ConsoleDependencyProvider as SprykerConsoleDependencyProvider;
use Spryker\Zed\SetupFrontend\Communication\Console\MerchantPortalBuildFrontendConsole;

class ConsoleDependencyProvider extends SprykerConsoleDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return array<\Symfony\Component\Console\Command\Command>
     */
    protected function getConsoleCommands(Container $container): array
    {
        $commands = [
            new MerchantPortalBuildFrontendConsole(),
        ];

        return $commands;
    }
}
  1. Add the Merchant Portal build commands:
  • build-static-production:
merchant-portal-build-frontend:
    command: 'vendor/bin/console frontend:mp:build -e production'
    timeout: 1600
  • build-static-development:
merchant-portal-build-frontend:
    command: 'vendor/bin/console frontend:mp:build'
    timeout: 1600

7) Set up ACL behavior

Each feature and module with a persistent relation to the merchant must expand the ACL configuration. Based on your built-in features, integrate the required plugins.

Integrate ACL rule plugins

PLUGIN SPECIFICATION PREREQUISITES NAMESPACE
DashboardMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds dashboard-merchant-portal-gui to the list of AclRules. Spryker\Zed\DashboardMerchantPortalGui\Communication\Plugin\AclMerchantPortal
MerchantProfileMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds merchant-profile-merchant-portal-gui to the list of AclRules. Spryker\Zed\MerchantProfileMerchantPortalGui\Communication\Plugin\AclMerchantPortal
ProductOfferMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds product-offer-merchant-portal-gui to the list of AclRules. Spryker\Zed\ProductOfferMerchantPortalGui\Communication\Plugin\AclMerchantPortal
ProductMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds product-merchant-portal-gui to the list of AclRules. Spryker\Zed\ProductMerchantPortalGui\Communication\Plugin\AclMerchantPortal
SalesMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds sales-merchant-portal-gui to the list of AclRules. Spryker\Zed\SalesMerchantPortalGui\Communication\Plugin\AclMerchantPortal
DummyMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds dummy-merchant-portal-gui to the list of AclRules. Spryker\Zed\DummyMerchantPortalGui\Communication\Plugin\AclMerchantPortal
PriceProductMerchantRelationshipMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds price-product-merchant-relationship-merchant-portal-gui to the list of AclRules. Spryker\Zed\PriceProductMerchantRelationshipMerchantPortalGui\Communication\Plugin\AclMerchantPortal
SecurityMerchantPortalGuiMerchantUserAclRuleExpanderPlugin Adds security-merchant-portal-gui to the list of AclRules. Spryker\Zed\SecurityMerchantPortalGui\Communication\Plugin\AclMerchantPortal
UserMerchantPortalGuiMerchantUserAclRuleExpanderPlugin Adds user-merchant-portal-gui to the list of AclRules. Spryker\Zed\UserMerchantPortalGui\Communication\Plugin\AclMerchantPortal
MerchantRelationshipMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds merchant-relationship-merchant-portal-gui to list of AclRules. Spryker\Zed\MerchantRelationshipMerchantPortalGui\Communication\Plugin\AclMerchantPortal
MerchantRelationRequestMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds merchant-relation-request-merchant-portal-gui to list of AclRules. Spryker\Zed\MerchantRelationRequestMerchantPortalGui\Communication\Plugin\AclMerchantPortal
CommentMerchantPortalGuiMerchantAclRuleExpanderPlugin Adds comment-merchant-portal-gui to list of AclRules. Spryker\Zed\CommentMerchantPortalGui\Communication\Plugin\AclMerchantPortal
MerchantUserMerchantUserAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with the merchant user composite data. Spryker\Zed\MerchantUser\Communication\Plugin\AclMerchantPortal

Integrate ACL entity rule expander plugins

PLUGIN SPECIFICATION PREREQUISITES NAMESPACE
ProductOfferMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with product offer composite data. Spryker\Zed\ProductOffer\Communication\Plugin\AclMerchantPortal
ProductMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with product composite data. Spryker\Zed\Product\Communication\Plugin\AclMerchantPortal
MerchantMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with merchant composite data. Spryker\Zed\Merchant\Communication\Plugin\AclMerchantPortal
MerchantSalesOrderMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with merchant sales order composite data. Spryker\Zed\MerchantSalesOrder\Communication\Plugin\AclMerchantPortal
MerchantProductMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with merchant product composite data. Spryker\Zed\MerchantProduct\Communication\Plugin\AclMerchantPortal
CurrencyMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with currency composite data. Spryker\Zed\Currency\Communication\Plugin\AclMerchantPortal
CountryMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with country composite data. Spryker\Zed\Country\Communication\Plugin\AclMerchantPortal
StoreMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with store composite data. Spryker\Zed\Store\Communication\Plugin\AclMerchantPortal
LocaleMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with locale composite data. Spryker\Zed\Locale\Communication\Plugin\AclMerchantPortal
SalesMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with sales composite data. Spryker\Zed\Sales\Communication\Plugin\AclMerchantPortal
PriceProductMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with price product composite data. Spryker\Zed\PriceProduct\Communication\Plugin\AclMerchantPortal
ProductImageMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with product image composite data. Spryker\Zed\ProductImage\Communication\Plugin\AclMerchantPortal
OmsProductOfferReservationMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with OMS product offer reservation composite data. Spryker\Zed\OmsProductOfferReservation\Communication\Plugin\AclMerchantPortal
OmsMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with OMS composite data. Spryker\Zed\Oms\Communication\Plugin\AclMerchantPortal
UrlMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with URL composite data. Spryker\Zed\Url\Communication\Plugin\AclMerchantPortal
RefundMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with refund composite data. Spryker\Zed\Refund\Communication\Plugin\AclMerchantPortal
CustomerMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with customer composite data. Spryker\Zed\Customer\Communication\Plugin\AclMerchantPortal
DiscountMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with discount composite data. Spryker\Zed\Discount\Communication\Plugin\AclMerchantPortal
DiscountPromotionMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with discount promotion composite data. Spryker\Zed\DiscountPromotion\Communication\Plugin\AclMerchantPortal
MerchantRelationRequestMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with merchant relation request composite data. Spryker\Zed\MerchantRelationRequest\Communication\Plugin\AclMerchantPortal
MerchantRelationshipMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with merchant relationship composite data. Spryker\Zed\MerchantRelationship\Communication\Plugin\AclMerchantPortal
CompanyUnitAddressMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with company unit address composite data. Spryker\Zed\CompanyUnitAddress\Communication\Plugin\AclMerchantPortal
CompanyBusinessUnitMerchantAclEntityRuleExpanderPlugin Expands a set of AclEntityRule transfer objects with company business unit composite data. Spryker\Zed\CompanyBusinessUnit\Communication\Plugin\AclMerchantPortal

Integrate ACL configuration plugins

PLUGIN SPECIFICATION PREREQUISITES NAMESPACE
AclEntityAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\AclEntity\Communication\Plugin\AclMerchantPortal
AclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Communication\Plugin\AclMerchantPortal
AvailabilityAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Availability\Communication\Plugin\AclMerchantPortal
CategoryAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Category\Communication\Plugin\AclMerchantPortal
CategoryImageAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\CategoryImage\Communication\Plugin\AclMerchantPortal
CmsBlockAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\CmsBlock\Communication\Plugin\AclMerchantPortal
CommentAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Comment\Communication\Plugin\AclMerchantPortal
CompanyBusinessUnitAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\CompanyBusinessUnit\Communication\Plugin\AclMerchantPortal
CompanyUnitAddressAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\CompanyUnitAddress\Communication\Plugin\AclMerchantPortal
CompanyBusinessUnitModelAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\CompanyBusinessUnit\Communication\Plugin\AclMerchantPortal
CountryAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Country\Communication\Plugin\AclMerchantPortal
CurrencyAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Currency\Communication\Plugin\AclMerchantPortal
DiscountAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Discount\Communication\Plugin\AclMerchantPortal
DiscountPromotionAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\DiscountPromotion\Communication\Plugin\AclMerchantPortal
GiftCardAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\GiftCard\Communication\Plugin\AclMerchantPortal
GlossaryAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Glossary\Communication\Plugin\AclMerchantPortal
LocaleAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Locale\Communication\Plugin\AclMerchantPortal
MerchantAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Merchant\Communication\Plugin\AclMerchantPortal
MerchantCategoryAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantCategory\Communication\Plugin\AclMerchantPortal
MerchantProductAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantProduct\Communication\Plugin\AclMerchantPortal
MerchantProductOfferAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantProductOffer\Communication\Plugin\AclMerchantPortal
MerchantProfileAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantProfile\Communication\Plugin\AclMerchantPortal
MerchantRelationshipModelAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantRelationship\Communication\Plugin\AclMerchantPortal
MerchantSalesOrderAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantSalesOrder\Communication\Plugin\AclMerchantPortal
MerchantStockAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantStock\Communication\Plugin\AclMerchantPortal
MerchantUserAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\MerchantUser\Communication\Plugin\AclMerchantPortal
OmsAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Oms\Communication\Plugin\AclMerchantPortal
OmsProductOfferReservationAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\OmsProductOfferReservation\Communication\Plugin\AclMerchantPortal
PaymentAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Payment\Communication\Plugin\AclMerchantPortal
PriceProductAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\PriceProduct\Communication\Plugin\AclMerchantPortal
PriceProductMerchantRelationshipAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\PriceProductMerchantRelationship\Communication\Plugin\AclMerchantPortal
PriceProductOfferAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\PriceProductOffer\Communication\Plugin\AclMerchantPortal
ProductAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Product\Communication\Plugin\AclMerchantPortal
ProductAttributeAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductAttribute\Communication\Plugin\AclMerchantPortal
ProductCategoryAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductCategory\Communication\Plugin\AclMerchantPortal
ProductImageAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductImage\Communication\Plugin\AclMerchantPortal
ProductOfferAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductOffer\Communication\Plugin\AclMerchantPortal
ProductOfferStockAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductOfferStock\Communication\Plugin\AclMerchantPortal
ProductOfferValidityAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductOfferValidity\Communication\Plugin\AclMerchantPortal
ProductOptionAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductOption\Communication\Plugin\AclMerchantPortal
ProductSearchAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductSearch\Communication\Plugin\AclMerchantPortal
ProductValidityAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\ProductValidity\Communication\Plugin\AclMerchantPortal
RefundAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Refund\Communication\Plugin\AclMerchantPortal
SalesAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Sales\Communication\Plugin\AclMerchantPortal
SalesInvoiceAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\SalesInvoice\Communication\Plugin\AclMerchantPortal
SalesOrderThresholdAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\SalesOrderThreshold\Communication\Plugin\AclMerchantPortal
ShipmentAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Shipment\Communication\Plugin\AclMerchantPortal
StateMachineAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\StateMachine\Communication\Plugin\AclMerchantPortal
StockAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Stock\Communication\Plugin\AclMerchantPortal
StoreAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Store\Communication\Plugin\AclMerchantPortal
TaxAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Tax\Communication\Plugin\AclMerchantPortal
UrlAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\Url\Communication\Plugin\AclMerchantPortal
UserPasswordResetAclEntityConfigurationExpanderPlugin Expands the provided AclEntityMetadataConfig transfer object with composite data. Spryker\Zed\UserPasswordReset\Communication\Plugin\AclMerchantPortal
src/Pyz/Zed/AclMerchantPortal/AclMerchantPortalDependencyProvider.php
<?php

namespace Pyz\Zed\AclMerchantPortal;

use Spryker\Zed\Acl\Communication\Plugin\AclMerchantPortal\AclEntityConfigurationExpanderPlugin;
use Spryker\Zed\AclEntity\Communication\Plugin\AclMerchantPortal\AclEntityAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\AclMerchantPortal\AclMerchantPortalDependencyProvider as SprykerAclMerchantPortalDependencyProvider;
use Spryker\Zed\Availability\Communication\Plugin\AclMerchantPortal\AvailabilityAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Category\Communication\Plugin\AclMerchantPortal\CategoryAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\CategoryImage\Communication\Plugin\AclMerchantPortal\CategoryImageAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\CmsBlock\Communication\Plugin\AclMerchantPortal\CmsBlockAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Comment\Communication\Plugin\AclMerchantPortal\CommentAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\CommentMerchantPortalGui\Communication\Plugin\AclMerchantPortal\CommentMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\CompanyBusinessUnit\Communication\Plugin\AclMerchantPortal\CompanyBusinessUnitAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\CompanyBusinessUnit\Communication\Plugin\AclMerchantPortal\CompanyBusinessUnitMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\CompanyUnitAddress\Communication\Plugin\AclMerchantPortal\CompanyUnitAddressAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\CompanyUnitAddress\Communication\Plugin\AclMerchantPortal\CompanyUnitAddressMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\CompanyBusinessUnit\Communication\Plugin\AclMerchantPortal\CompanyBusinessUnitModelAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Country\Communication\Plugin\AclMerchantPortal\CountryAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Country\Communication\Plugin\AclMerchantPortal\CountryMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\Currency\Communication\Plugin\AclMerchantPortal\CurrencyAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Currency\Communication\Plugin\AclMerchantPortal\CurrencyMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\Customer\Communication\Plugin\AclMerchantPortal\CustomerMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\DashboardMerchantPortalGui\Communication\Plugin\AclMerchantPortal\DashboardMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\Discount\Communication\Plugin\AclMerchantPortal\DiscountAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Discount\Communication\Plugin\AclMerchantPortal\DiscountMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\AclMerchantPortal\DiscountPromotionAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\DiscountPromotion\Communication\Plugin\AclMerchantPortal\DiscountPromotionMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\DummyMerchantPortalGui\Communication\Plugin\AclMerchantPortal\DummyMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\GiftCard\Communication\Plugin\AclMerchantPortal\GiftCardAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Glossary\Communication\Plugin\AclMerchantPortal\GlossaryAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Locale\Communication\Plugin\AclMerchantPortal\LocaleAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Locale\Communication\Plugin\AclMerchantPortal\LocaleMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\Merchant\Communication\Plugin\AclMerchantPortal\MerchantAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Merchant\Communication\Plugin\AclMerchantPortal\MerchantMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\MerchantCategory\Communication\Plugin\AclMerchantPortal\MerchantCategoryAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantProduct\Communication\Plugin\AclMerchantPortal\MerchantProductAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantProduct\Communication\Plugin\AclMerchantPortal\MerchantProductMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\MerchantProductOffer\Communication\Plugin\AclMerchantPortal\MerchantProductOfferAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantProfile\Communication\Plugin\AclMerchantPortal\MerchantProfileAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantProfileMerchantPortalGui\Communication\Plugin\AclMerchantPortal\MerchantProfileMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\MerchantRelationRequest\Communication\Plugin\AclMerchantPortal\MerchantRelationRequestAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantRelationRequest\Communication\Plugin\AclMerchantPortal\MerchantRelationRequestMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\MerchantRelationRequestMerchantPortalGui\Communication\Plugin\AclMerchantPortal\MerchantRelationRequestMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\MerchantRelationship\Communication\Plugin\AclMerchantPortal\MerchantRelationshipMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\MerchantRelationship\Communication\Plugin\AclMerchantPortal\MerchantRelationshipModelAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantRelationshipMerchantPortalGui\Communication\Plugin\AclMerchantPortal\MerchantRelationshipMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\MerchantSalesOrder\Communication\Plugin\AclMerchantPortal\MerchantSalesOrderAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantSalesOrder\Communication\Plugin\AclMerchantPortal\MerchantSalesOrderMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\MerchantStock\Communication\Plugin\AclMerchantPortal\MerchantStockAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantUser\Communication\Plugin\AclMerchantPortal\MerchantUserAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\MerchantUser\Communication\Plugin\AclMerchantPortal\MerchantUserMerchantUserAclEntityRuleExpanderPlugin;
use Spryker\Zed\Oms\Communication\Plugin\AclMerchantPortal\OmsAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Oms\Communication\Plugin\AclMerchantPortal\OmsMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\OmsProductOfferReservation\Communication\Plugin\AclMerchantPortal\OmsProductOfferReservationAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\OmsProductOfferReservation\Communication\Plugin\AclMerchantPortal\OmsProductOfferReservationMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\Payment\Communication\Plugin\AclMerchantPortal\PaymentAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\PriceProduct\Communication\Plugin\AclMerchantPortal\PriceProductAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\PriceProduct\Communication\Plugin\AclMerchantPortal\PriceProductMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\PriceProductMerchantRelationship\Communication\Plugin\AclMerchantPortal\PriceProductMerchantRelationshipAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\PriceProductMerchantRelationshipMerchantPortalGui\Communication\Plugin\AclMerchantPortal\PriceProductMerchantRelationshipMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\PriceProductOffer\Communication\Plugin\AclMerchantPortal\PriceProductOfferAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Product\Communication\Plugin\AclMerchantPortal\ProductAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Product\Communication\Plugin\AclMerchantPortal\ProductMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\ProductAttribute\Communication\Plugin\AclMerchantPortal\ProductAttributeAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductCategory\Communication\Plugin\AclMerchantPortal\ProductCategoryAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductImage\Communication\Plugin\AclMerchantPortal\ProductImageAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductImage\Communication\Plugin\AclMerchantPortal\ProductImageMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\ProductMerchantPortalGui\Communication\Plugin\AclMerchantPortal\ProductMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\ProductOffer\Communication\Plugin\AclMerchantPortal\ProductOfferAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductOffer\Communication\Plugin\AclMerchantPortal\ProductOfferMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\ProductOfferMerchantPortalGui\Communication\Plugin\AclMerchantPortal\ProductOfferMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\ProductOfferStock\Communication\Plugin\AclMerchantPortal\ProductOfferStockAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductOfferValidity\Communication\Plugin\AclMerchantPortal\ProductOfferValidityAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductOption\Communication\Plugin\AclMerchantPortal\ProductOptionAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductSearch\Communication\Plugin\AclMerchantPortal\ProductSearchAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\ProductValidity\Communication\Plugin\AclMerchantPortal\ProductValidityAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Refund\Communication\Plugin\AclMerchantPortal\RefundAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Refund\Communication\Plugin\AclMerchantPortal\RefundMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\Sales\Communication\Plugin\AclMerchantPortal\SalesAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Sales\Communication\Plugin\AclMerchantPortal\SalesMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\SalesInvoice\Communication\Plugin\AclMerchantPortal\SalesInvoiceAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\SalesMerchantPortalGui\Communication\Plugin\AclMerchantPortal\SalesMerchantPortalGuiMerchantAclRuleExpanderPlugin;
use Spryker\Zed\SalesOrderThreshold\Communication\Plugin\AclMerchantPortal\SalesOrderThresholdAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\SecurityMerchantPortalGui\Communication\Plugin\AclMerchantPortal\SecurityMerchantPortalGuiMerchantUserAclRuleExpanderPlugin;
use Spryker\Zed\Shipment\Communication\Plugin\AclMerchantPortal\ShipmentAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\StateMachine\Communication\Plugin\AclMerchantPortal\StateMachineAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Stock\Communication\Plugin\AclMerchantPortal\StockAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Store\Communication\Plugin\AclMerchantPortal\StoreAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Store\Communication\Plugin\AclMerchantPortal\StoreMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\Tax\Communication\Plugin\AclMerchantPortal\TaxAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Url\Communication\Plugin\AclMerchantPortal\UrlAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Url\Communication\Plugin\AclMerchantPortal\UrlMerchantAclEntityRuleExpanderPlugin;
use Spryker\Zed\UserMerchantPortalGui\Communication\Plugin\AclMerchantPortal\UserMerchantPortalGuiMerchantUserAclRuleExpanderPlugin;
use Spryker\Zed\UserPasswordReset\Communication\Plugin\AclMerchantPortal\UserPasswordResetAclEntityConfigurationExpanderPlugin;

class AclMerchantPortalDependencyProvider extends SprykerAclMerchantPortalDependencyProvider
{
    /**
     * @return list<\Spryker\Zed\AclMerchantPortalExtension\Dependency\Plugin\MerchantAclRuleExpanderPluginInterface>
     */
    protected function getMerchantAclRuleExpanderPlugins(): array
    {
        return [
            new DashboardMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new MerchantProfileMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new ProductOfferMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new ProductMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new SalesMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new DummyMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new PriceProductMerchantRelationshipMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new MerchantRelationRequestMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new CommentMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
            new MerchantRelationshipMerchantPortalGuiMerchantAclRuleExpanderPlugin(),
        ];
    }

    /**
     * @return list<\Spryker\Zed\AclMerchantPortalExtension\Dependency\Plugin\MerchantAclEntityRuleExpanderPluginInterface>
     */
    protected function getMerchantAclEntityRuleExpanderPlugins(): array
    {
        return [
            new ProductOfferMerchantAclEntityRuleExpanderPlugin(),
            new ProductMerchantAclEntityRuleExpanderPlugin(),
            new MerchantMerchantAclEntityRuleExpanderPlugin(),
            new MerchantSalesOrderMerchantAclEntityRuleExpanderPlugin(),
            new MerchantProductMerchantAclEntityRuleExpanderPlugin(),
            new CurrencyMerchantAclEntityRuleExpanderPlugin(),
            new CountryMerchantAclEntityRuleExpanderPlugin(),
            new StoreMerchantAclEntityRuleExpanderPlugin(),
            new LocaleMerchantAclEntityRuleExpanderPlugin(),
            new SalesMerchantAclEntityRuleExpanderPlugin(),
            new PriceProductMerchantAclEntityRuleExpanderPlugin(),
            new ProductImageMerchantAclEntityRuleExpanderPlugin(),
            new OmsProductOfferReservationMerchantAclEntityRuleExpanderPlugin(),
            new OmsMerchantAclEntityRuleExpanderPlugin(),
            new UrlMerchantAclEntityRuleExpanderPlugin(),
            new RefundMerchantAclEntityRuleExpanderPlugin(),
            new CustomerMerchantAclEntityRuleExpanderPlugin(),
            new DiscountMerchantAclEntityRuleExpanderPlugin(),
            new DiscountPromotionMerchantAclEntityRuleExpanderPlugin(),
            new MerchantRelationRequestMerchantAclEntityRuleExpanderPlugin(),
            new MerchantRelationshipMerchantAclEntityRuleExpanderPlugin(),
            new CompanyUnitAddressMerchantAclEntityRuleExpanderPlugin(),
            new CompanyBusinessUnitMerchantAclEntityRuleExpanderPlugin(),
        ];
    }

    /**
     * @return list<\Spryker\Zed\AclMerchantPortalExtension\Dependency\Plugin\MerchantUserAclRuleExpanderPluginInterface>
     */
    protected function getMerchantUserAclRuleExpanderPlugins(): array
    {
        return [
            new SecurityMerchantPortalGuiMerchantUserAclRuleExpanderPlugin(),
            new UserMerchantPortalGuiMerchantUserAclRuleExpanderPlugin(),
        ];
    }

    /**
     * @return list<\Spryker\Zed\AclMerchantPortalExtension\Dependency\Plugin\MerchantUserAclEntityRuleExpanderPluginInterface>
     */
    protected function getMerchantUserAclEntityRuleExpanderPlugins(): array
    {
        return [
            new MerchantUserMerchantUserAclEntityRuleExpanderPlugin(),
        ];
    }

    /**
     * @return list<\Spryker\Zed\AclMerchantPortalExtension\Dependency\Plugin\AclEntityConfigurationExpanderPluginInterface>
     */
    protected function getAclEntityConfigurationExpanderPlugins(): array
    {
        return [
            new AclEntityAclEntityConfigurationExpanderPlugin(),
            new AclEntityConfigurationExpanderPlugin(),
            new AvailabilityAclEntityConfigurationExpanderPlugin(),
            new CategoryAclEntityConfigurationExpanderPlugin(),
            new CategoryImageAclEntityConfigurationExpanderPlugin(),
            new CmsBlockAclEntityConfigurationExpanderPlugin(),
            new CommentAclEntityConfigurationExpanderPlugin(),
            new CompanyBusinessUnitAclEntityConfigurationExpanderPlugin(),
            new CompanyUnitAddressAclEntityConfigurationExpanderPlugin(),
            new CompanyBusinessUnitModelAclEntityConfigurationExpanderPlugin(),
            new CountryAclEntityConfigurationExpanderPlugin(),
            new CurrencyAclEntityConfigurationExpanderPlugin(),
            new DiscountAclEntityConfigurationExpanderPlugin(),
            new DiscountPromotionAclEntityConfigurationExpanderPlugin(),
            new GiftCardAclEntityConfigurationExpanderPlugin(),
            new GlossaryAclEntityConfigurationExpanderPlugin(),
            new LocaleAclEntityConfigurationExpanderPlugin(),
            new MerchantAclEntityConfigurationExpanderPlugin(),
            new MerchantCategoryAclEntityConfigurationExpanderPlugin(),
            new MerchantProductAclEntityConfigurationExpanderPlugin(),
            new MerchantProductOfferAclEntityConfigurationExpanderPlugin(),
            new MerchantProfileAclEntityConfigurationExpanderPlugin(),
            new MerchantRelationshipModelAclEntityConfigurationExpanderPlugin(),
            new MerchantSalesOrderAclEntityConfigurationExpanderPlugin(),
            new MerchantStockAclEntityConfigurationExpanderPlugin(),
            new MerchantUserAclEntityConfigurationExpanderPlugin(),
            new OmsAclEntityConfigurationExpanderPlugin(),
            new OmsProductOfferReservationAclEntityConfigurationExpanderPlugin(),
            new PaymentAclEntityConfigurationExpanderPlugin(),
            new PriceProductAclEntityConfigurationExpanderPlugin(),
            new PriceProductMerchantRelationshipAclEntityConfigurationExpanderPlugin(),
            new PriceProductOfferAclEntityConfigurationExpanderPlugin(),
            new ProductAclEntityConfigurationExpanderPlugin(),
            new ProductAttributeAclEntityConfigurationExpanderPlugin(),
            new ProductCategoryAclEntityConfigurationExpanderPlugin(),
            new ProductImageAclEntityConfigurationExpanderPlugin(),
            new ProductOfferAclEntityConfigurationExpanderPlugin(),
            new ProductOfferStockAclEntityConfigurationExpanderPlugin(),
            new ProductOfferValidityAclEntityConfigurationExpanderPlugin(),
            new ProductOptionAclEntityConfigurationExpanderPlugin(),
            new ProductSearchAclEntityConfigurationExpanderPlugin(),
            new ProductValidityAclEntityConfigurationExpanderPlugin(),
            new RefundAclEntityConfigurationExpanderPlugin(),
            new SalesAclEntityConfigurationExpanderPlugin(),
            new SalesInvoiceAclEntityConfigurationExpanderPlugin(),
            new SalesOrderThresholdAclEntityConfigurationExpanderPlugin(),
            new ShipmentAclEntityConfigurationExpanderPlugin(),
            new StateMachineAclEntityConfigurationExpanderPlugin(),
            new StockAclEntityConfigurationExpanderPlugin(),
            new StoreAclEntityConfigurationExpanderPlugin(),
            new TaxAclEntityConfigurationExpanderPlugin(),
            new UrlAclEntityConfigurationExpanderPlugin(),
            new UserPasswordResetAclEntityConfigurationExpanderPlugin(),
            new MerchantRelationRequestAclEntityConfigurationExpanderPlugin(),
        ];
    }
}
Verification

Verify that all propel-related entities with the merchant have the allowed or restricted access level according to the ACL configuration.

Adjust environment infrastructure

Merchant Portal security

The Merchant Portal should be exposed separately from the Back Office. The Merchant Portal must not have OS, DNS name, VirtualHost settings, FileSystem, and service credentials shared with the Back Office.

To adjust environment infrastructure, take the following steps.

1) Set up a docker container for the Merchant Portal

  • MerchantPortal must be placed into its own private subnet and must have access to the following:

    • Primary database
    • Message broker
  • MerchantPortal must not have access to the following:

    • Search and Storage
    • Gateway
    • Scheduler
  • To set up a docker container, update the needed deploy file. Example:

deploy.dev.yml
...
image:
    ...
    node:
        version: 18
        npm: 9
...
groups:
    EU:
        region: EU
        applications:
            merchant_portal_eu:
                application: merchant-portal
                endpoints:
                    mp.de.spryker.local:
                        entry-point: MerchantPortal
                        store: DE
                        primal: true
                        services:
                            session:
                                namespace: 7
                    mp.at.spryker.local:
                        entry-point: MerchantPortal
                        store: AT
                        services:
                            session:
                                namespace: 8
    US:
        region: US
        applications:
            merchant_portal_us:
                application: merchant-portal
                endpoints:
                    mp.us.spryker.local:
                        entry-point: MerchantPortal
                        store: US
                        services:
                            session:
                                namespace: 9

2) Create a database user for the Merchant Portal

  • Grant only default CRUD operations: INSERT, DELETE, UPDATE, and SELECT.
  • Don’t grant ALL PRIVILEGES, GRANT OPTION, DROP, CREATE, and other admin-related grants.

Create a user for a MySQL database:

>CREATE USER 'merchantportal'@'localhost' IDENTIFIED BY '{your_merchantportal_password}'; // Make sure to change the password
GRANT SELECT, INSERT, UPDATE, DELETE ON your_app_schema.* TO 'merchantportal'@'localhost';
FLUSH PRIVILEGES;

3) Create Nginx web server configuration

  1. Add Nginx configuration. Example:

/etc/nginx/merchant-portal.conf

server {
    # { Your virtual host settings }

    # Allow /assets/js/mp assets to be served only
    location ~ (/assets/js/mp|/favicon.ico|/robots.txt) {
        access_log        off;
        expires           30d;
        add_header Pragma public;
        add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        try_files $uri =404;
    }

   # Allow /marchant-portal-gui pages to be served only
   location ~ ^/[a-z-]+-merchant-portal-gui {
        add_header X-Server $hostname;
        fastcgi_pass { YOUR_FASTCGI_PASS };
        fastcgi_index index.php;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_NAME /index.php;
        fastcgi_param APPLICATION_ENV $application_env;
        fastcgi_param APPLICATION_STORE $application_store;
        fastcgi_param SCRIPT_FILENAME  $document_root/index.php;

        # Credentials of the newly created DB user.
        fastcgi_param SPRYKER_DB_USERNAME merchantportal;
        fastcgi_param SPRYKER_DB_PASSWORD '{your_merchantportal_password}';


        more_clear_headers 'X-Powered-By' 'X-Store' 'X-Locale' 'X-Env' 'Server';
    }
}
  1. Apply the config:f:
sudo service nginx reload

Make sure to use environment variables in config-default.php:

config/Shared/config_default.php

<?php

// other code

$config[PropelConstants::ZED_DB_USERNAME] = getenv('SPRYKER_DB_USERNAME');
$config[PropelConstants::ZED_DB_PASSWORD] = getenv('SPRYKER_DB_PASSWORD');
Verification
  • The Merchant Portal login page is available at https://your-merchant-portal.domain/security-merchant-portal-gui/login.

  • The following pages are not available:

    • https://your-merchant-portal.domain/security-gui/login
    • https://your-merchant-portal.domain/.

4) Register modules in ACL

  1. Add the following modules to installer rules:

src/Pyz/Zed/Acl/AclConfig.php

<?php

namespace Pyz\Zed\Acl;

use Spryker\Shared\Acl\AclConstants;
use Spryker\Zed\Acl\AclConfig as SprykerAclConfig;

class AclConfig extends SprykerAclConfig
{
    /**
     * @param array<array<string>> $installerRules
     *
     * @return array<array<string>>
     */
    protected function addMerchantPortalInstallerRules(array $installerRules): array
    {
        $bundleNames = [
            'user-merchant-portal-gui',
            'dashboard-merchant-portal-gui',
            'security-merchant-portal-gui',
        ];

        foreach ($bundleNames as $bundleName) {
            $array<installerRules> = [
                'bundle' => $bundleName,
                'controller' => AclConstants::VALIDATOR_WILDCARD,
                'action' => AclConstants::VALIDATOR_WILDCARD,
                'type' => static::RULE_TYPE_DENY,
                'role' => AclConstants::ROOT_ROLE,
            ];
        }

        return $installerRules;
    }
}
  1. Register the modules and infrastructural data:
console setup:init-db
Verification

Make sure the user-merchant-portal-gui rule has been added to the spy_acl_rule table.

5) Update navigation

  1. Add the My Account and Logout sections to Merchant Portal navigation:

config/Zed/navigation-secondary-merchant-portal.xml

<?xml version="1.0"?>
<config>
    <my-account>
        <label>My Account</label>
        <title>My Account</title>
        <bundle>user-merchant-portal-gui</bundle>
        <controller>my-account</controller>
        <action>index</action>
    </my-account>
    <logout>
        <label>Logout</label>
        <title>Logout</title>
        <bundle>security-merchant-portal-gui</bundle>
        <controller>logout</controller>
        <action>index</action>
        <type>danger</type>
    </logout>
</config>
  1. Build the navigation cache:
console navigation:build-cache
Verification
  1. Log into the Merchant Portal.
  2. Click the profile picture.
  3. In the overlay of the secondary navigation, make sure the My Account and Logout buttons are displayed.
FEATURE REQUIRED FOR THE CURRENT FEATURE INSTALLATION GUIDE
Merchant Portal Install the Merchant Portal