Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
saptexpert
Explorer
There could be infinite ways to design a content-rich and user-friendly web application using SAP UI5 libraries. However designing such an application keeping the core functionality in the back-end ERP intact is a challenging task, especially when it comes to the ESS Benefits Enrollment.

This blog discusses about the key design challenges and the solution architecture that was identified and followed while pursuing a mobile friendly web application for ESS Benefits Enrollment using SAP_UI and HR-BN Components.

Objective




Digital transformation of the ESS benefits enrollment application into an user friendly, device and browser agnostic web application using SAP UI5 libraries, keeping the HR-BN component at it's core.

Background




We did not wanted to re-invent the wheel or design a whole benefits module from scratch. The idea is to leverage all the functionality, config and the processing logic offered by SAP while making some enhancements and additions on top of it to adapt with the new UI and vice versa too. In other words, the UI and the back-end functionality must support each other adapting and adjusting to each other's needs.

For our productive usage, our main area of focus was on the following categories, we can extend the solution to other categories and plan types as well if needed.

  • Health Plans & Dependents

  • Savings Accounts

  • Spending Accounts

  • Insurance plans

  • Miscellaneous

  • Stock Plans


Challenges





  1. All the underlying benefits configuration, rules, validations and the processing logic must be retained, while keeping an open eye to all possible future changes.

  2. The standard enqueue and dequeue must be retained to ensure the data is not corrupted while the users are actively working on their benefits.

  3. Optimizing the application run time and avoiding long running busy indicators.


 

Solution in a Nutshell






 

Persistence Data Store




A custom repository (Enrollment Cart) was created to act as the persistence storage of the selections and the changes made by the users to their benefits using the generated offer. We took the most common and required attributes from the below structures to build this repository.

  • RPBENOFFER

  • RPBEN_CONTRIB_TRANS

  • RPBEN_CONTRIBUTIONS

  • RPBEN_CONTRIB_LIMITS

  • RPBENDEP


The data in this repository is temporary and will be wiped off as soon as the user exits out of the application. This is similar to the persistence achieved using shared memory buffer in the FPM based ABAP Webdynpro application today.

 

Gateway Service




A brand new custom gateway service was designed and developed to supply the data to the front-end UI. Each category has it's own infotype and thereby needs an entity within the gateway model.

The model is based on a single benefits area and all the properties are mostly imported from the standard DDIC structures (RPBEN*) available in SAP. The relationships/associations between each parent and child entities are created using the common key attributes from all the infotypes.

  • Adjustment Reasons → Plan Types

  • Plan Types → Health Plans

  • Health Plans → Dependents

  • Plan Types → Savings

  • Plan Types → Insurance

  • Plan Types → Spending

  • Plan Types → Stock

  • Plan Types → Miscellaneous

  • Adjustment Reasons → Enrollment Cart


The service is made a soft-state enabled to create Lock (enqueue) on the employees using the BAPI_EMPLOYEE_ENQUEUE in the first OData entityset call made to the gateway service by the front end UI5 application.

There are several standard SAP delivered function modules and RFC's that can help you get the offer, participating plans, validate selections and save the changes to infotypes (some of the important ones are listed below). They need to be called in a certain sequence to keep the offer, selections and changes consistent and clean throughout the app lifetime.

  • HR_BEN_ESS_RFC_ENRO_REASONS → To get list of eligible adjustment reasons

  • HR_BEN_ESS_GET_LATEST_OFFER → To obtain offer for selected enrollment event

  • HR_BEN_ESS_GET_OVERVIEW_DATA → To get an overview of current participation

  • HR_BEN_ESS_MODIFY_PLANS  → To add/update the selection from the offer

  • HR_BEN_ESS_CHECK_CONSISTENCY → To perform comprehensive validation

  • HR_BEN_ESS_SAVE_PLANS → To submit the selections to Infotypes


Key Consideration -

Make sure that the employee selections are added/updated to the cart and are included for validations before and after the update for a comprehensive check of the plans and to verify any pre-requisites or co-requisites. → Addresses Challenge 1

 

UI5 Application




The UI5 applications is designed in such a way that the number of HTTP/oData calls to the back-end are optimized and used only during the key instances when there is a need. As you can see from the picture above, the calls are triggered only during 4 events.

The app initial load will fetch the list of eligible adjustment reasons for the employee while also creating a lock for the PREL. If the Lock fails, the app should let user know that the enrollment cannot be possible at the time.

This lock stays on, as long as the user resides in the app and a webRFC is called in the event the user exits out of the app or closes the browser window to unlock PREL for the employee. We chose a webRFC call using $.ajax over a regular oData call here. Addresses Challenge 2

You can also use the browser window event 'unload', if the event 'beforeunload' doesn't work.
$(window).on("beforeunload", function () {
/* Locked is a local indicator set to true after the Lock was successful during initial load */
if (Locked === true) {
$.ajax({
type: "GET",
cache: false,
async: false,
url: "/sap/bc/webrfc?_FUNCTION=<FM to unlock PREL>&_odataSrv=/sap/opu/odata/sap/<GW Service>",
success: function(oSuccess) {/* Set local Lock indicator as false if needed */},
error: function(oError){}
});
}
});

 

The selection of an adjustment reason brings the latest offer for the employee and all the data is stored in global JSON models for each infotype/category involved. These global JSON models can be stored globally for the app runtime using sap.ui.getCore().setModel() and can be retrieved whenever needed for binding or creating local copies using sap.ui.getCore().getModel().
/* onInit method of planTypes controller*/
var oModel = new sap.ui.model.json.JSONModel();
oModel.setDefaultBindingMode(sap.ui.model.BindingMode.OneWay);
sap.ui.getCore().setModel(oModel, "globalOffer");
this.getRouter().getRoute("planTypes").attachPatternMatched(this._onPatternMatched, this);

/* onPatternMatched for planTypes */
var gOffer = sap.ui.getCore().getModel("globalOffer");
if (gOffer.getData() === null || !gOffer.getData().results) {
var cResults = getOffer(<Event>,<Pernr>,<EntitySet Name>);
cResults.then(function (data) {
sap.ui.getCore().getModel("globalOffer").setData(data);
}.bind(this));
}

/* method to fetch offer from the gateway sevrice in the back-end */
getOffer: function (oEvent, oPernr, oEntitySetName) {
var oModel = this.getModel();
var sPath = "/EnrollReasons(Pernr='" + oPernr + "',Event='" + oEvent + "')/" + oEntitySetName;
var promise = jQuery.Deferred();
oModel.read(sPath, {
async: true,
success: function (oData, response) {promise.resolve(response.data);},
error: function (oError){} });
return promise;
}

 

An individual view and an associating controller was created for each benefit category to display eligible plans offered and similarly for their respective detail/dependent selections. Whenever the user navigates to the plans overview or specific detail/dependent view, a local copy of the global JSON model is created and adjusted based on the data in Enrollment cart and the binding is refreshed.

We used the jquery $.extend function to create a local copy and adjust the offer data based on the data already stored in the cart. → Addresses Challenge 3
var gResults, lResults = [];
gResults = sap.ui.getCore().getModel("globalOffer").getData();
lResults = $.extend(true, {}, oResults);
checkCart(lResults); // Method to adjust local offer based on selections in the cart
this.getView().getModel("localOffer").setData(lResults);
this.getView().getModel("localOffer").refresh();

 

Key Considerations -

Anytime the user navigates to Landing Page or exits out of the app, the JSON models stored in the global memory are cleared and the enrollment cart is refreshed.

Try to bind the individual elements on the detail views with the attributes/properties supplied from the back-end entity (Check structures RPBEN_SCREEN_CTRL, RPBENOFFER etc..) which will take care of most of the things usually driven by config.

For Eg: A  form grouped with pre-tax contribution fields should be displayed or hidden based on the indicator if pre-tax contribution is allowed.

Some of the benefit categories (Savings, Stock & Miscellaneous) share a similar cost/contribution structure to a certain extent, so we can leverage that to re-use certain form fields and

 

WebRFC ↔ Consumer proxy ↔ Service Provider




You may be wondering why such a complex architecture was designed to simply unlock a locked PREL object. Here are the reasons..

  1. In a productive landscape, there are most likely more than 1 app servers running on the instance.

  2. The lock may have been created in a session running on an app server different from the one that was selected by the web dispatcher for the last call made to the back-end during exit.

  3. Employee may not be having the necessary authorizations to read or kill the sessions on all app servers available on the instance.


In order to trigger an unlock without any problems in any of the above mentioned or unmentioned scenarios, the idea of a killing the soft-state session using a proxy service user came up.

So the WebRFC triggers a consumer proxy configured with a logical port (authenticated by a service user that can read and unlock from multiple app servers) which in turn calls a web service created as a wrapper around an RFC function that reads all the app servers, finds the gateway oData session which contains the lock and kills it.

The RFC function module contains the logic to read all the sessions from the instance, identifies the server on which the session is running, calls itself using the RFC destination (created with the same name as the app server) to execute a system command to kill the session.
  INCLUDE: tskhincl.
DATA(lv_path) = iv_path.
TRANSLATE lv_path TO UPPER CASE.

DATA with_application_info TYPE ssi_bool.
DATA tenant TYPE ssi_tenant_id.
DATA session_list TYPE ssi_session_list.
DATA server_name TYPE ssi_servername.
DATA actual_server TYPE ssi_servername.
DATA rfcdest TYPE rfcdest.

rc = 4.

TRY.
session_list = cl_system_info=>get_session_list(
with_application_info = 1
tenant = sy-mandt
).
CATCH cx_ssi_no_auth.
ENDTRY.

server_name = cl_abap_syst=>get_instance_name( ).

LOOP AT session_list ASSIGNING FIELD-SYMBOL(<fs_session_info>)
WHERE user_name = iv_user
AND logon_type = 3
AND logon_sub_type = 4
AND act_program = 'SAPMHTTP'.

TRANSLATE <fs_session_info>-application_info TO UPPER CASE.

IF <fs_session_info>-application_info CS lv_path.
actual_server = <fs_session_info>-server_name. CONDENSE actual_server.
IF server_name EQ actual_server.
CALL 'ThUsrInfo' ID 'OPCODE' FIELD opcode_delete_usr
ID 'TID' FIELD <fs_session_info>-logon_hdl
ID 'LOGON_ID' FIELD <fs_session_info>-logon_id.
IF sy-subrc <> 0.
CONTINUE.
ELSE.
rc = 0.
ENDIF.
ELSE.
rfcdest = <fs_session_info>-server_name.
CALL FUNCTION <self FM> DESTINATION rfcdest
EXPORTING
iv_path = iv_path
iv_user = iv_user
IMPORTING
rc = rc.
CONTINUE.
ENDIF.
ENDIF.
ENDLOOP.

 

Conclusion/s




Creating a persistence data store for storing the enrollment selections temporarily helped with keeping the data sacred and consistent.

Creating a stateful session using the soft-state mode helped retain the locks and killing that sessions in the exit event using a proxy service user releases the lock.

Creating JSON Models in the global and local memory of the app run time helped optimize the number of oData calls at only key events during the app life time.

You may or may not need the exact design or similar architecture for your apps, there could be several other ways that can be pursued to achieve a similar feat. However the motive behind the blog is to share the experiences of creating a mobile friendly UI5 based ESS Benefits Enrollment web application, which might inspire fellow customers, functional consultants and developers working on the HR-BN Module offered by SAP.
1 Comment
Labels in this area