RAP - Validation
Let's take a look at the validations in the RAP Business Object and how they are implemented in the framework.
Table of contents
In the business object, validations ensure that the data is consistent at all times, centrally across all actions that are carried out on the object. In this article we look at how to create such validations and how they work exactly.
General
Validations belong to the behavioral definition of a business object and are defined at the corresponding entity, where they should also apply. They are triggered when the data is saved and can be registered for the individual events such as create, update and delete. Furthermore, there is also the possibility that a validation is only triggered when a single field is changed.
General validation
The general validation refers to validations that are always triggered when saving certain events (Create, Update, Delete), see the following example.
Create
Let's define the first validation in an example. To do this, we begin by expanding the behavior definition:
validation validateKeyIsFilled on save { create; }
A validation is created with the keyword "validation", followed by the name of the validation and the event at which it is triggered. In almost all cases this is "on save", i.e. when saving the data in the save sequence. In the curly brackets we now state that the event only occurs when a data record is created. After enabling the behavior definition, we get the following warning message in Eclipse:
The implementation of the method in the behavior implementation (class) is still missing. You can easily create this by placing the cursor on the name of the validation and creating the method with CTRL + 1 in the Quick Assist. At the end you automatically end up in the class and the created method.
CLASS lhc_Partner DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS validatekeyisfilled FOR VALIDATE ON SAVE
IMPORTING keys FOR partner~validatekeyisfilled.
ENDCLASS.
CLASS lhc_Partner IMPLEMENTATION.
METHOD validateKeyIsFilled.
ENDMETHOD.
ENDCLASS.
Implement
Before we start implementing the method, let's take a closer look at the interface. As you can see in the definition above, a parameter has been defined in the method interface, let's now check this in the signature:
In addition to the keys, you also have "failed" and "reported" available, which indirectly belong to the method.
- KEYS - Passing the keys to be checked to the method.
- FAILED - return the keys that failed.
- REPORTED - Return the erroneous keys and messages describing the error.
Now let's implement the check in the method. Since this is a simple mandatory field check, the logic is correspondingly simple and within the loop we generate the error message:
LOOP AT keys INTO DATA(ls_key) WHERE PartnerNumber IS INITIAL.
INSERT VALUE #( PartnerNumber = ls_key-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_key-PartnerNumber
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'PartnerNumber is mandatory' )
) INTO TABLE reported-partner.
ENDLOOP.
There are two helper methods for filling the message, which are available through inheritance. With "new_message" we can create a T100 message and with "new_message_with_text" a simple text is enough to output a message:
The message can then be output directly in the UI in the lower left corner. According to the classification via "severity", we can influence the color and the icon (as in the SAP GUI):
Field level validation
The next validation is only triggered when the corresponding field changes. This is particularly useful when we check against interfaces or tables whether this value exists in order to save performance.
Create
As in the first example of the article, the system is created via the behavior definition and the subsequent generation in the behavior implementation. This time we always check when the two master data fields change. From a performance point of view, we should then rather make two validations out of it:
validation validateCoreData on save { field Country, PaymentCurrency; }
Implement
We already looked at the method's interface in the first example, so you'll notice that only the keys are passed to the method. Now, to get the missing information, we need to read the data via EML. We can then loop over the data and compare it against the master data.
READ ENTITIES OF ZBS_I_RAPPartner IN LOCAL MODE
ENTITY Partner
FIELDS ( Country PaymentCurrency )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_partner_data)
FAILED DATA(ls_failed)
REPORTED DATA(ls_reported).
LOOP AT lt_partner_data INTO DATA(ls_partner).
SELECT SINGLE FROM I_Country
FIELDS Country
WHERE Country = @ls_partner-Country
INTO @DATA(ld_found_country).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Country not found in I_Country' )
%element-country = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
SELECT SINGLE FROM I_Currency
FIELDS Currency
WHERE Currency = @ls_partner-PaymentCurrency
INTO @DATA(ld_found_currency).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Currency not found in I_Currency' )
%element-paymentcurrency = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
ENDLOOP.
In the example we used a few things that we want to explain again here:
- READ - When reading, we didn't specify the key in the field selector, but it's automatically read as it's used to identify the entity.
- LOCAL MODE - The verification of the user and the status is omitted, the system assumes that this was done before. Possible blocks by the user will be avoided.
- %ELEMENT - When generating the error messages, we also specified and activated the element, which ensures that the field is also marked and the error message appears.
Complete
If you try the validation, you will find that it does not work when creating the entry. The set validation only reacts to changes in the field, which is not the case with the system since the data record does not yet exist. To do this, we also have to include the create, so that at least the validation is also carried out during the creation.
validation validateCoreData on save { create; field Country, PaymentCurrency; }
Complete example
The full example behavior definition and implementation can be found here.
Behavior definition
managed implementation in class zbp_bs_demo_rappartner unique;
strict;
define behavior for ZBS_I_RAPPartner alias Partner
persistent table zbs_dmo_partner
lock master
authorization master ( instance )
{
create;
update;
delete;
validation validateKeyIsFilled on save { create; }
validation validateCoreData on save { create; field Country, PaymentCurrency; }
mapping for zbs_dmo_partner
{
PartnerNumber = partner;
PartnerName = name;
Street = street;
City = city;
Country = country;
PaymentCurrency = payment_currency;
}
}
Behavior implementation
CLASS lhc_Partner DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
IMPORTING keys REQUEST requested_authorizations FOR Partner RESULT result.
METHODS validatekeyisfilled FOR VALIDATE ON SAVE
IMPORTING keys FOR partner~validatekeyisfilled.
METHODS validatecoredata FOR VALIDATE ON SAVE
IMPORTING keys FOR partner~validatecoredata.
ENDCLASS.
CLASS lhc_Partner IMPLEMENTATION.
METHOD get_instance_authorizations.
ENDMETHOD.
METHOD validateKeyIsFilled.
LOOP AT keys INTO DATA(ls_key) WHERE PartnerNumber IS INITIAL.
INSERT VALUE #( PartnerNumber = ls_key-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_key-PartnerNumber
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'PartnerNumber is mandatory' )
) INTO TABLE reported-partner.
ENDLOOP.
ENDMETHOD.
METHOD validateCoreData.
READ ENTITIES OF ZBS_I_RAPPartner IN LOCAL MODE
ENTITY Partner
FIELDS ( Country PaymentCurrency )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_partner_data)
FAILED DATA(ls_failed)
REPORTED DATA(ls_reported).
LOOP AT lt_partner_data INTO DATA(ls_partner).
SELECT SINGLE FROM I_Country
FIELDS Country
WHERE Country = @ls_partner-Country
INTO @DATA(ld_found_country).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Country not found in I_Country' )
%element-country = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
SELECT SINGLE FROM I_Currency
FIELDS Currency
WHERE Currency = @ls_partner-PaymentCurrency
INTO @DATA(ld_found_currency).
IF sy-subrc <> 0.
INSERT VALUE #( PartnerNumber = ls_partner-PartnerNumber ) INTO TABLE failed-partner.
INSERT VALUE #(
PartnerNumber = ls_partner-PartnerNumber
%msg = new_message_with_text( text = 'Currency not found in I_Currency' )
%element-paymentcurrency = if_abap_behv=>mk-on
) INTO TABLE reported-partner.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
Conclusion
The validations are a powerful tool to keep your data clean within the business object and to ensure that only valid information is stored. Accordingly, when creating your RAP BO, you should consider which information should be validated and when.