Skip to content

Customer Portal applications

New business customers can apply for a company account. Applications go through the approval process in the back office where they can be accepted, rejected or put on hold. If they're accepted, the business partner receives an invitation link to the Customer Portal, where they can set up their team and manage their account.

For more information on company self-registration, see user guide documentation. If provided options are too limited, you can customize an approval process by yourself.

Roles and policies

Any user can become application approver, as long as they have the Company Application/Workflow policy assigned to their role. There, you can define between which states the user may move applications. For example, the assistant can put new applications on hold, or reject them, and only the manager can accept them.

Company Application policy

Customer Portal application configuration

Below, you can find possible configurations for Customer Portal applications.

Reasons for rejecting application

To change or add reasons for not accepting Corporate Portal application go to vendor/ibexa/corporate-account/src/bundle/Resources/config/default_settings.yaml.

1
2
3
4
parameters:
    ibexa.site_access.config.default.corporate_accounts.reasons:
        reject: [Malicious intent / Spam]
        on_hold: [Verification in progress]

Timeout

Registration form locks for 5 minutes after unsuccessful registration, if the user, for example, tried to use an email address that already exists in a Customer Portal clients database. To change that duration, go to config/packages/ibexa.yaml.

1
2
3
4
5
6
7
framework:
    rate_limiter:
        corporate_account_application:
            policy: 'fixed_window'
            limit: 1
            interval: '5 minutes'
            lock_factory: 'lock.corporate_account_application.factory'

Customization of an approval process

In this procedure, you add a new status to the approval process of business account application.

Add new status

First, under the ibexa.system.<scope>.corporate_accounts.application.states add a verify status to the configuration:

1
2
3
4
5
6
ibexa:
    system:
        default:
            corporate_accounts:
                application:
                    states: [ 'new', 'accept', 'on_hold', 'reject', 'verify' ]

Create new Form Type

Next, create a new form type in src/Form/VerifyType.php. It's displayed in the application review stage.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

declare(strict_types=1);

namespace App\Corporate\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

final class VerifyType extends AbstractType
{
    private const FIELD_APPLICATION = 'application';
    private const FIELD_NOTES = 'notes';
    private const FIELD_VERIFY = 'verify';

    public function buildForm(
        FormBuilderInterface $builder,
        array $options
    ): void {
        $builder
            ->add(self::FIELD_APPLICATION, HiddenType::class)
            ->add('new_field', TextType::class)
            ->add(self::FIELD_NOTES, TextareaType::class, [
                'required' => false,
            ])
            ->add(self::FIELD_VERIFY, SubmitType::class);
    }
}

Line 29 defines where the form should be displayed, line 21 adds Note field, and line 22 adds the Verify button.

Create event subscriber to pass the form

Add an event subscriber that passes a new form type to the frontend. Create src/Corporate/EventSubscriber/ApplicationDetailsViewSubscriber.php following the example below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php

declare(strict_types=1);

namespace App\Corporate\EventSubscriber;

use App\Corporate\Form\VerifyType;
use Ibexa\Bundle\CorporateAccount\EventSubscriber\AbstractViewSubscriber;
use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface;
use Ibexa\Core\MVC\Symfony\View\View;
use Ibexa\CorporateAccount\View\ApplicationDetailsView;
use Symfony\Component\Form\FormFactoryInterface;

final class ApplicationDetailsViewSubscriber extends AbstractViewSubscriber
{
    private FormFactoryInterface $formFactory;

    public function __construct(
        SiteAccessServiceInterface $siteAccessService,
        FormFactoryInterface $formFactory
    ) {
        parent::__construct($siteAccessService);

        $this->formFactory = $formFactory;
    }

    /**
     * @param \Ibexa\CorporateAccount\View\ApplicationDetailsView $view
     */
    protected function configureView(View $view): void
    {
        $application = $view->getApplication();

        $view->addParameters([
            'verify_form' => $this->formFactory->create(
                VerifyType::class,
                [
                    'application' => $application->getId(),
                ]
            )->createView(),
        ]);
    }

    protected function supports(View $view): bool
    {
        return $view instanceof ApplicationDetailsView;
    }
}

In line 39, you can see the verify_form parameter that passes the verify form to the application review view.

Add form template

To be able to see the changes you need to add a new template templates/themes/admin/corporate_account/application/details.html.twig.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{% extends "@IbexaCorporateAccount/themes/admin/corporate_account/application/details.html.twig" %}

{% block content %}
    {{ form_start(verify_form, { action: path('ibexa.corporate_account.application.workflow.state', {
        state: 'verify',
        applicationId: application.id,
    }), method: 'POST'}) }}
    {{ form_row(verify_form.notes) }}

    <div class="ibexa-ca-application-workflow-extra-actions__btns">
        {{ form_widget(verify_form.verify, { attr: {
            class: 'ibexa-btn ibexa-btn--primary ibexa-ca-application-workflow-extra-actions__btn',
        }}) }}
    </div>
    {{ form_end(verify_form) }}

    {{ parent() }}
{% endblock %}

It overrides the default view and adds a Verify button to the review view. To check the progress, go to Members -> Applications. Select one application from the list and inspect application review view for a new button.

Verify button

Create event subscriber to verify state

Now, you need to pass the information that the button has been selected to the list of applications to change the application status. Create another event subscriber that passes the information from the created form to the application list src/Corporate/EventSubscriber/VerifyStateEventSubscriber.php.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?php

declare(strict_types=1);

namespace App\Corporate\EventSubscriber;

use App\Corporate\Form\VerifyType;
use Ibexa\Contracts\AdminUi\Notification\TranslatableNotificationHandlerInterface;
use Ibexa\Contracts\CorporateAccount\Event\Application\Workflow\ApplicationWorkflowFormEvent;
use Ibexa\Contracts\CorporateAccount\Event\Application\Workflow\MapApplicationWorkflowFormEvent;
use Ibexa\CorporateAccount\Event\ApplicationWorkflowEvents;
use Ibexa\CorporateAccount\Persistence\Legacy\ApplicationState\HandlerInterface;
use Ibexa\CorporateAccount\Persistence\Values\ApplicationStateUpdateStruct;
use JMS\TranslationBundle\Annotation\Desc;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormFactoryInterface;

final class VerifyStateEventSubscriber implements EventSubscriberInterface
{
    private const VERIFY_STATE = 'verify';

    private FormFactoryInterface $formFactory;

    private HandlerInterface $applicationStateHandler;

    private TranslatableNotificationHandlerInterface $notificationHandler;

    public function __construct(
        FormFactoryInterface $formFactory,
        HandlerInterface $applicationStateHandler,
        TranslatableNotificationHandlerInterface $notificationHandler
    ) {
        $this->formFactory = $formFactory;
        $this->applicationStateHandler = $applicationStateHandler;
        $this->notificationHandler = $notificationHandler;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            MapApplicationWorkflowFormEvent::class => 'mapApplicationWorkflowForm',
            ApplicationWorkflowEvents::getStateEvent(self::VERIFY_STATE) => 'applicationVerify',
        ];
    }

    public function mapApplicationWorkflowForm(MapApplicationWorkflowFormEvent $event): void
    {
        if ($event->getState() === self::VERIFY_STATE) {
            $form = $this->formFactory->create(VerifyType::class, $event->getData());

            $event->setForm($form);
        }
    }

    public function applicationVerify(ApplicationWorkflowFormEvent $event): void
    {
        $data = $event->getData();

        if (!is_array($data)) {
            return;
        }

        $applicationStateUpdateStruct = new ApplicationStateUpdateStruct($event->getApplicationState()->getId());
        $applicationStateUpdateStruct->state = self::VERIFY_STATE;

        $this->applicationStateHandler->update($applicationStateUpdateStruct);

        $this->notificationHandler->success(
            /** @Desc("Application moved to Verification state") */
            'application.state.verify.notification',
            [],
            'corporate_account_application'
        );
    }
}

In line 46, you can see that it handles changes to verify status. The subscriber only informs that the status has been changed (line 72).

Now, if you click the Verify button during application review, the application gets Verify status.

Verify status