Create a new Backoffice Perspective

This blog is intended to provide basic steps and knowledge to create a new backoffice perspective. This is advantageous in scenarios when the use case requires a separate set of users to perform specific functionalities which can be handled effectively with a different perspective without modifying the Administration Cockpit.

For instance, consider a scenario where you are selling service to the customers, and you have a dedicated backend team who can approve any service request, modify the request, assign service agents, handle tickets related to the services, and so on. In this case, a separate backoffice perspective can be created for the backend team to handle the functionalities. This will not create any unnecessary modification on the admin backoffice, and the backend team can only see and work with the items necessary for their activities.

Pre-requisites

  1. This is a technical blog, so the reader should have working experience of SAP Commerce. He should have a Commerce version installed in his local.
  2. The reader should also have basic backoffice knowledge, the uses of widgets and configs, widget connections, etc.

High-level steps to create a backoffice perspective:

  1. Create a Backoffice Role, User Group and an Employee for that user group.
  2. (Optional) Create a perspective chooser  in backoffice-config.xml.
  3. Create a perspective switcher  in backoffice-config.xml.
  4. (Optional) Create notification-area.
  5. Define a separate explorer-tree for the new perspective.
  6. (Optional) Define listViewActions in backoffice-config.xml.
  7. (Optional) Define editorAreaActions in backoffice-config.xml.
  8. Define widget for the new perspective in the backoffice-widgets.xml under backofficeMainSlot widget.
  9. Define widget connections for each of the new widgets created – perspective-checker, node-selector, explorerTree, refineBy, advancedSearch, advancedSearchEngine, and so on.
  10. Define proper labelling wherever necessary.

We will now discuss in details for each of the steps mentioned above.

Create a User Role, User Group and an Employee for that user group

This step is required if we plan to separate out the perspective completely, like the Customer Support Backoffice.

First, we have to create a BackofficeRole which will have authorities defined. This is required perspective-view-switcher, so that if an employee with this role logs in with id and password, he will directly land on the Custom Perspective. The backoffice role created should be a part of Employee group.

Next, we have to create a UserGroup, which should be a member of the role created above. Under this group, create an Employee, and we will be set with the user details required to login to the new perspective.

Additionally, UserRights should be defined for the new UserGroup.

Create a perspective chooser

The Backoffice Perspective Chooser widget displays available perspectives that can be selected. Sample backoffice code:

<context component="perspective-chooser" principal="orgemployeerole" merge-by="module" parent="commerceservicesbackoffice"> <y:perspective-chooser xmlns:y="http://www.hybris.com/cockpitng/config/perspectiveChooser"> <y:defaultPerspective name="organizationBackofficePerspective"/> <y:authority name="orgemployeerole"> <y:perspective id="organizationBackofficePerspective"/> <y:perspective id="Custom-Perspective"/> </y:authority> </y:perspective-chooser>
</context>

Create a perspective switcher 

The perspective switcher maps the perspective to the user role created. This is needed to log into the Custom Perspective directly once you provide the user credentials. Sample backoffice code:

<context component="perspective-view-switcher" principal="customagentrole"> <vs:view-switcher xmlns:vs="http://www.hybris.com/cockpitng/config/viewSwitcher"> <vs:authority name="customagentrole"> <vs:view id="Custom-Perspective"/> </vs:authority> </vs:view-switcher>
</context>

Create notification-area

The notification area gives you a space on the top section of backoffice view, where success and failure notifications can be displayed. It is similar to how the notification area looks for the admin perspective.

Notification%20Area

Notification Area

Define a separate explorer-tree

A separate explorer tree needs to be defined for the Custom Perspective. This will hold the items necessary to be displayed on the explorer tree in the new perspective, and will not interfere with the Admin one. The component name should match with the value provided for explorerTreeConfigCtx under the explorerTree widget, defined in backoffice-widgets.xml.

The widget should also have proper widget-connections defined.

Sample code in backoffice-config.xml:

<context component="pnm-explorer-tree" module="custombackoffice" principal="customagentrole"> <n:explorer-tree xmlns:n="http://www.hybris.com/cockpitng/config/explorertree" title="custom"> <n:type-node code="Airport" id="custom_backoffice_explorerTree_airport"/> <n:type-node code="Terminal" id="custom_backoffice_explorerTree_terminal"/> </n:explorer-tree>
</context>

Sample code in backoffice-widgets.xml:

<widget id="pnmExplorerTreeContainer" widgetDefinitionId="com.hybris.cockpitng.borderlayout" slotId="centerSlot" template="false"> <widget id="pnmExplorerTree" widgetDefinitionId="com.hybris.cockpitng.widgets.common.explorertree" slotId="centerSlot" template="false" title="custom_backoffice_perspective_name"> <widget id="pnmTreeSelectionConditionEvaluator" widgetDefinitionId="com.hybris.cockpitng.conditionevaluator" slotId="cockpitWidgetChildrenInvisible" title="pnmTreeSelectionConditionEvaluator" template="false"> <setting key="explorerTreeConfigCtx" type="String">pnm-explorer-tree</setting> <setting key="widgetStyleClass" type="String">y-custom-explorer-tree</setting> <setting key="allowFilteringEntries" type="Boolean">false</setting> <setting key="widgetStyleAttribute" type="String"></setting> <virtual-sockets/> </widget> </widget>
</widget>
<widget-connection sourceWidgetId="backofficeMainSlot" outputId="perspectiveSelected" targetWidgetId="pnm-perspective-checker" inputId="input"/>
<widget-connection sourceWidgetId="pnm-perspective-checker" outputId="true" targetWidgetId="pnm-node-selector" inputId="genericInput"/>
<widget-connection sourceWidgetId="pnm-node-selector" outputId="genericOutput" targetWidgetId="pnmExplorerTree" inputId="nodeIdSelected"/> <widget-connection sourceWidgetId="pnmExplorerTree" outputId="nodeSelected" targetWidgetId="customRefineBy" inputId="nodeSelected"/>
<widget-connection sourceWidgetId="pnmExplorerTree" outputId="nodeSelected" targetWidgetId="pnmTreeSelectionConditionEvaluator" inputId="input"/>
.....

Define listViewActions and editorAreaActions

These actions are required to have a list of action buttons in the list view and in the editor area sections. These are optional, and dependent on the requirement. actionSlotComponentId under the widget collectionBrowser should have the value which gets used to define the component in backoffice-config.xml.

<setting key="actionSlotComponentId" type="String">pnmlistviewactions</setting>
<context component="pnmlistviewactions" type="GenericItem"> <y:actions xmlns:y="http://www.hybris.com/cockpit/config/hybris"> <y:group qualifier="common"> <y:label>actiongroup.common</y:label> <y:action action-id="com.hybris.cockpitng.action.delete" property="selectedObjects"/> <y:action action-id="com.hybris.cockpitng.action.create" property="pageable.typeCode"/> </y:group> </y:actions>
</context>

Define widget for the new perspective in the backoffice-widgets.xml under backofficeMainSlot widget

In the backoffice-widgets.xml, you need to define proper widgets. Under the backofficeMainSlot the Custom-Perspective should be defined first, with slotId=”perspectives”. Inside this custom widget, all the remaining widget definition for leftSlotContainer, collabsibleContainer, etc. needs to be defined. explorer-tree, editor-area, simpleSearch configuration, advanced-search and other required widgets and settings should be defined accordingly. Sample backoffice-widgets.xml code:

<widget-extension widgetId="backofficeMainSlot"> <widget id="Custom-Perspective" widgetDefinitionId="com.hybris.cockpitng.borderlayout" slotId="perspectives" title="custom_backoffice_perspective_name" template="false" access="customagentrole"> <widget id="pnm-leftSlotContainer" widgetDefinitionId="com.hybris.cockpitng.borderlayout" slotId="leftSlot" template="false"> ... ... ... </widget> <widget id="pnmCollapsibleContainer" widgetDefinitionId="com.hybris.cockpitng.collapsiblecontainer" slotId="centerSlot" template="false"> <widget id="pnmAdvancedSearch" widgetDefinitionId="com.hybris.cockpitng.advancedsearch" slotId="center" template="false"> <widget id="pnmAdvancedSearchEngine" widgetDefinitionId="com.hybris.cockpitng.widgets.common.advancedsearchengine" slotId="cockpitWidgetChildrenInvisible" template="false"> ... ... </widget> <widget id="pnmCollectionBrowser" widgetDefinitionId="com.hybris.cockpitng.collectionBrowser" slotId="nestedWidget" title="csCollectionBrowser" template="false"> <setting key="itemRenderer" type="String">listViewRenderer</setting> <setting key="actionSlotComponentId" type="String">pnmlistviewactions</setting> <setting key="colConfigCtxCode" type="String">pnm-listview</setting> ... ... </widget> <setting key="simpleSearchConfigCtxCode" type="String">simple-search</setting> <setting key="advancedSearchConfigCtxCode" type="String">advanced-search</setting> <setting key="disableAutoSearch" type="Boolean">false</setting> <setting key="disableAdvancedSearchToolbar" type="Boolean">false</setting> ... ... </widget> <widget id="pnmEditorArea" widgetDefinitionId="com.hybris.cockpitng.backoffice.defaultEditorArea" slotId="bottom" title="pnmEditorArea" template="false"> ... ... <widget id="pnmBackofficeSpaceManagement" widgetDefinitionId="com.hybris.backoffice.spaceManagement" slotId="cockpitWidgetChildrenInvisible" template="false"> <setting key="widgetStyleAttribute" type="String"></setting> <setting key="widgetStyleClass" type="String"></setting> <virtual-sockets/> </widget> ... ... </widget> ... ... </widget> <setting key="autoCloseInactive" type="Boolean">false</setting> <setting key="widgetStyleClass" type="String"></setting> <setting key="widgetStyleAttribute" type="String"></setting> <virtual-sockets/>
</widget-extension>

NOTE: This is sample code, so I have kept reference for sample widgets and settings and virtual-sockets. This will depend on the exact requirement and perspective view expected in your case.

Define widget connections for each of the new widgets created

Once widgets are defined, there should be seamless widget-connection definition, so that each widget should have inputId, outputId and targetWidgetId. Without these, flow and navigation will not function properly in backoffice. Sample code:

<widget-connection sourceWidgetId="backofficeMainSlot" outputId="perspectiveSelected" targetWidgetId="pnm-perspective-checker" inputId="input"/>
<widget-connection sourceWidgetId="pnm-perspective-checker" outputId="true" targetWidgetId="pnm-node-selector" inputId="genericInput"/>
<widget-connection sourceWidgetId="pnm-node-selector" outputId="genericOutput" targetWidgetId="pnmExplorerTree" inputId="nodeIdSelected"/> <widget-connection sourceWidgetId="pnmExplorerTree" outputId="nodeSelected" targetWidgetId="customRefineBy" inputId="nodeSelected"/>
<widget-connection sourceWidgetId="pnmExplorerTree" outputId="nodeSelected" targetWidgetId="pnmTreeSelectionConditionEvaluator" inputId="input"/>
<widget-connection sourceWidgetId="pnmTreeSelectionConditionEvaluator" outputId="true" targetWidgetId="pnmNamePropExtractor" inputId="genericInput"/>
<widget-connection sourceWidgetId="pnmNamePropExtractor" outputId="genericOutput" targetWidgetId="pnmAdvancedSearch" inputId="type"/>
<widget-connection sourceWidgetId="pnmAdvancedSearch" outputId="searchData" targetWidgetId="pnmAdvancedSearchEngine" inputId="searchData"/>
<widget-connection sourceWidgetId="pnmAdvancedSearch" outputId="searchData" targetWidgetId="pnmBackofficeSpaceManagement" inputId="searchData"/>
<widget-connection sourceWidgetId="pnmBackofficeSpaceManagement" outputId="collapseState" targetWidgetId="pnmCollapsibleContainer" inputId="collapseState"/>
<widget-connection sourceWidgetId="pnmAdvancedSearchEngine" outputId="pageable" targetWidgetId="pnmCollectionBrowser" inputId="pageable"/> <widget-connection sourceWidgetId="pnmCollectionBrowser" outputId="focusedItem" targetWidgetId="pnmBackofficeSpaceManagement" inputId="listSelectedObject"/>
<widget-connection sourceWidgetId="pnmCollectionBrowser" outputId="focusedItem" targetWidgetId="pnmEditorArea" inputId="inputObject"/>
<widget-connection sourceWidgetId="pnmCollectionBrowser" outputId="sortData" targetWidgetId="pnmAdvancedSearch" inputId="sortData"/> <widget-connection sourceWidgetId="STUB_com.hybris.cockpitng.editor.simpleselectreferenceeditor" outputId="referenceEditorOutput" targetWidgetId="configurableFlow" inputId="context"/>
<widget-connection sourceWidgetId="STUB_com.hybris.cockpitng.editor.simpleselectreferenceeditor" outputId="itemSelected" targetWidgetId="csSessionContext" inputId="selectedItem"/>
<widget-connection sourceWidgetId="STUB_com.hybris.cockpitng.editor.simpleselectreferenceeditor" outputId="itemSelected" targetWidgetId="pnmEditorArea" inputId="inputObject"/> <widget-connection sourceWidgetId="STUB_com.hybris.cockpitng.editor.simpleselectlisteditor" outputId="referenceEditorOutput" targetWidgetId="configurableFlow" inputId="context"/>
<widget-connection sourceWidgetId="STUB_com.hybris.cockpitng.editor.simpleselectlisteditor" outputId="itemSelected" targetWidgetId="pnmEditorArea" inputId="inputObject"/>

Localization

Based on the titles and the labels used in backoffice-config.xml and backoffice-widgets.xml, proper localization should be handled in the labels_<lang>.properties inside your custombackoffice-backoffice-labels directory.

Result

Once all these things are in place, build and start the server.

If you have created impexes for the BackofficeRole, UserGroup, Employee and User Rights, then those impexes should be executed, either manually, or by Update System.

Backoffice view needs to be adjusted, either by Update System with backoffice extensions selected, or by reset everything option after navigating to orchestrate mode.

After all the previous steps are done, login to backoffice with the user id and password for the newly created employee. You should be able to view the new perspective you have created. Sample perspective view is shown below, with the help of a screenshot:

Additional NOTES:

So far I have explained the creation of a new perspective. Once the perspective is created, you can define additional features and items such as action buttons, renderers, editors, custom widgets, etc. They will work in the same way as in the admin backoffice perspective. However, proper widget and container names should be defined, and they should have the correct module and principal mentioned, wherever necessary. Sample code:

<context type="Airport" component="base" merge-by="type" parent="GenericItem" principal="customagentrole"> <y:base> <y:labels> <y:label>code</y:label> </y:labels> </y:base>
</context>

Here, you can see that principal has been mentioned as customagentrole.

References:

There are multiple help.sap references depicting the functionality of each widgets and their structure. I am mentioning some of the required links below:

I hope I have been able to explain and throw some light on the steps and process to create a new backoffice perspective to effectively handle custom requirements.

Please share your thoughts and feedback in the comments section below. Do look out for more new blog posts.