Hello Everyone,
In this blog we will be going to learn about the Validations in BOPF.
What is Validation in BOPF?
In BOPF, a validation is a consistency check defined on a node of the business object that ensures the data fulfills specific business rules before it is saved or processed.
In BOPF, keeping the data clean and consistent is very important. Validations help achieve this by checking the data against specific rules. Just like determinations, validations are created on the node of the data model where they are needed. Today, we’ll walk you through how to create a validation and write the logic for it in the implementation class.
Procedure
Here we are defining a custom Travel table (zkk_dt_travel).
@EndUserText.label : ‘Data base table for Travel Details’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zkk_dt_travel {
key client : abap.clnt not null;
key travel_id : zkk_de_travel_id not null;
key user_email : zkk_de_email not null;
start_date : dats;
destination : zkk_de_destination;
status : zkk_de_status;
expense_type : zkk_de_item_type;
@Semantics.amount.currencyCode : ‘zkk_travel_hdr.curr_key’
total_cost : zkk_de_cost;
curr_key : zkk_de_cost_key;
remarks : zkk_de_remarks;
created_by : zkk_de_createdby;
created_on : datum;
}
The above table has the below shown data.
For the Database table we are defining the Basic view
@AbapCatalog.sqlViewName: ‘ZKK_DBV_TRAVEL’
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Interface view for Travel details’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel:{
modelCategory: #BUSINESS_OBJECT,
compositionRoot: true,
transactionalProcessingEnabled: true,
writeActivePersistence: ‘ZKK_dt_travel’,
createEnabled: true,
updateEnabled: true,
deleteEnabled: true
}
@OData.publish: true
define view zi_kk_travel
as select from zkk_dt_travel
{
key travel_id,
@ObjectModel.mandatory: true
key user_email,
start_date,
destination,
status,
@ObjectModel.mandatory: true
expense_type,
@ObjectModel.mandatory: true
total_cost,
curr_key,
remarks,
created_by,
created_on
}
For creating the Business object directly from the Eclipse editor, we have to make use of annotations which I have given with their functionality
Here’s the explanation of each annotation in your snippet:
@ObjectModel: {
modelCategory: #BUSINESS_OBJECT,
compositionRoot: true,
transactionalProcessingEnabled: true,
writeActivePersistence: ‘ZKK_dt_travel’,
createEnabled: true,
updateEnabled: true,
deleteEnabled: true
}
Explanation
modelCategory: #BUSINESS_OBJECT
Declares that the CDS view represents a Business Object (BO). This means it can be used in BOPF to handle business logic and transactional operations.
compositionRoot: true
Defines this entity as the root node of the Business Object. Other nodes (child entities) can be linked to it via composition relationships.
transactionalProcessingEnabled: true
Enables transactional behavior (create, update, delete, save, rollback) for the entity, making it suitable for business object processing.
writeActivePersistence: ‘ZKK_dt_travel’
Specifies the database table (ZKK_DT_TRAVEL) where the active (persisted) data of this entity is stored.
createEnabled: true
Allows the creation of new records in the entity.
updateEnabled: true
Allows modification of existing records.
deleteEnabled: true
Allows deletion of records.
@odata.publish: true turns your CDS view into an OData service provider, making its data available for consumption via OData protocols.
In short:
This annotation block is used to define a CDS entity as a Business Object root node with full CRUD (Create, Read, Update, Delete) support and persistence in the custom table ZKK_DT_TRAVEL.
Here I am creating the Metadata extension for the Basic view (ZI_KK_TRAVEL) for consuming it in the front end.
@Metadata.layer: #CORE
@UI.headerInfo: {
typeName: ‘Travel’,
typeNamePlural: ‘Travel Details’
}
annotate entity zi_kk_travel
with
{
@UI.facet: [{
id: ‘Travel’,
purpose: #STANDARD,
position: 10,
label: ‘Travel Details ‘,
type: #IDENTIFICATION_REFERENCE
} ]
@UI.lineItem: [{ position: 10, label: ‘Travel Id’ }]
@UI.identification: [{ position: 10 }]
travel_id;
@UI.lineItem: [{ position: 20, label: ‘Email’ }]
@UI.identification: [{ position: 20 }]
user_email;
@UI.lineItem: [{ position: 30, label: ‘Travel Date’ }]
@UI.identification: [{ position: 30 }]
start_date;
@UI.lineItem: [{ position: 40, label: ‘Destination’ }]
@UI.identification: [{ position: 40 }]
destination;
@UI.lineItem: [{ position: 50, label: ‘Status’ }]
@UI.identification: [{ position: 50 }]
status;
@UI.lineItem: [{ position: 60, label: ‘Expense Type’ }]
@UI.identification: [{ position: 60 }]
expense_type;
@UI.lineItem: [{ position: 65, label: ‘Amount’ }]
@UI.identification: [{ position: 65 }]
total_cost;
@UI.lineItem: [{ position: 70, label: ‘Currency’ }]
@UI.identification: [{ position: 70 }]
curr_key;
@UI.lineItem: [{ position: 80, label: ‘Remarks for Expense’ }]
@UI.identification: [{ position: 80 }]
remarks;
@UI.lineItem: [{ position: 85, label: ‘Created By’ }]
@UI.identification: [{ position: 10 }]
created_by;
@UI.lineItem: [{ position: 90, label: ‘Created Date’ }]
@UI.identification: [{ position: 90 }]
created_on;
}
Now activate the Basic view and the meta data extension, when it gets activated the Business Object get created implicitly.
Now open the BOBX Transaction.
We can search the business object in Home business objects or click on the below marked option.
Provide the BO name which is our Basic View name (ZI_KK_TRAVEL)
Now to Implement the validations expand the Node Elements
Right Click on Validations will see the option to Create Validations
Provide the Validation Name and Description and Class for implementing the validation logic.
Double click on Class Name, and create a class which will be having the super class implicitly
Now we have to provide Triggering conditions for the validations, so select the Node instance and tick on Create, Update, Delete and Check
Save it and check and generate it.
After this we have to go to eclipse and create a service definition on basic view (ZI_KK_TRAVEL)
Now create a Service binding on top of Service definition and publish it
When we preview it
It will be navigated to the frontend while navigating it will ask for your SAP GUI Credentials to login.
Now go to SE91 Transaction and create a message class
Here, I am doing Validation for a Single status field only so i have passed only two messages
Now I want to provide the Validations for Status field, so go to our class which we have created in Validations
Implement our Custom login in this method.
METHOD /bobf/if_frw_validation~execute.
CLEAR : et_failed_key, eo_message.
eo_message = /bobf/cl_frw_factory=>get_message( ).
DATA : lt_data TYPE ztikk_travel, ” Table type for header node
ls_msg TYPE symsg.
io_read->retrieve(
EXPORTING
iv_node = is_ctx-node_key
it_key = it_key
iv_fill_data = abap_true
it_requested_attributes = VALUE #(
( zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status )
) )
IMPORTING
et_data = lt_data
).
LOOP AT lt_data REFERENCE INTO DATA(ls_data).
IF ls_data->status IS INITIAL .
ls_msg-msgid = ‘ZKK_MSG_BOPF’.
ls_msg-msgno = ‘000’.
ls_msg-msgty = ‘E’.
eo_message->add_message(
is_msg = ls_msg ” Message that is to be added to the message object
iv_node = is_ctx-node_key ” Node to be used in the origin location
iv_key = ls_data->key ” Instance key to be used in the origin location
iv_attribute = zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status
” Attribute to be used in the origin location
).
INSERT VALUE #( key = ls_data->key ) INTO TABLE et_failed_key.
ENDIF.
DATA(lv_status) = to_upper( condense( ls_data->status ) ).
IF lv_status NA ‘ACCEPT’ OR lv_status NA ‘REJECT’.
ls_msg-msgid = ‘ZKK_MSG_BOPF’.
ls_msg-msgno = ‘001’.
ls_msg-msgty = ‘E’.
eo_message->add_message(
is_msg = ls_msg
iv_node = is_ctx-node_key
iv_key = ls_data->key
iv_attribute = zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status
).
INSERT VALUE #( key = ls_data->key ) INTO TABLE et_failed_key.
ENDIF.
ENDLOOP.
ENDMETHOD.
Now go to service binding and preview it. Try to create a new record.
Here I am keeping Status field as empty so it will throw error.
Now Status should accept only ‘ACCEPT’ or ‘REJECT’. If I try to give any other value it will throw error again.
If you give the correct value ‘Accept’ or ‘Reject’.
Conclusion
Validations in BOPF play a vital role in safeguarding data quality and enforcing business rules at the framework level. By defining validations directly on the business object nodes, we ensure that only consistent and compliant data enters the system, regardless of whether it originates from an SAP UI, an external interface, or a background process. This not only strengthens the reliability of the application but also reduces the need for redundant checks across different layers. In short, validations serve as a powerful and centralized mechanism to guarantee data integrity within the BOPF model.
Hello Everyone,In this blog we will be going to learn about the Validations in BOPF.What is Validation in BOPF? In BOPF, a validation is a consistency check defined on a node of the business object that ensures the data fulfills specific business rules before it is saved or processed. In BOPF, keeping the data clean and consistent is very important. Validations help achieve this by checking the data against specific rules. Just like determinations, validations are created on the node of the data model where they are needed. Today, we’ll walk you through how to create a validation and write the logic for it in the implementation class. Procedure Here we are defining a custom Travel table (zkk_dt_travel). @EndUserText.label : ‘Data base table for Travel Details’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zkk_dt_travel {
key client : abap.clnt not null;
key travel_id : zkk_de_travel_id not null;
key user_email : zkk_de_email not null;
start_date : dats;
destination : zkk_de_destination;
status : zkk_de_status;
expense_type : zkk_de_item_type;
@Semantics.amount.currencyCode : ‘zkk_travel_hdr.curr_key’
total_cost : zkk_de_cost;
curr_key : zkk_de_cost_key;
remarks : zkk_de_remarks;
created_by : zkk_de_createdby;
created_on : datum;
}The above table has the below shown data.For the Database table we are defining the Basic view @AbapCatalog.sqlViewName: ‘ZKK_DBV_TRAVEL’
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Interface view for Travel details’
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel:{
modelCategory: #BUSINESS_OBJECT,
compositionRoot: true,
transactionalProcessingEnabled: true,
writeActivePersistence: ‘ZKK_dt_travel’,
createEnabled: true,
updateEnabled: true,
deleteEnabled: true
}
@OData.publish: true
define view zi_kk_travel
as select from zkk_dt_travel
{
key travel_id,
@ObjectModel.mandatory: true
key user_email,
start_date,
destination,
status,
@ObjectModel.mandatory: true
expense_type,
@ObjectModel.mandatory: true
total_cost,
curr_key,
remarks,
created_by,
created_on
}For creating the Business object directly from the Eclipse editor, we have to make use of annotations which I have given with their functionality Here’s the explanation of each annotation in your snippet: @ObjectModel: { modelCategory: #BUSINESS_OBJECT, compositionRoot: true, transactionalProcessingEnabled: true, writeActivePersistence: ‘ZKK_dt_travel’, createEnabled: true, updateEnabled: true, deleteEnabled: true } Explanation modelCategory: #BUSINESS_OBJECT Declares that the CDS view represents a Business Object (BO). This means it can be used in BOPF to handle business logic and transactional operations. compositionRoot: true Defines this entity as the root node of the Business Object. Other nodes (child entities) can be linked to it via composition relationships. transactionalProcessingEnabled: true Enables transactional behavior (create, update, delete, save, rollback) for the entity, making it suitable for business object processing. writeActivePersistence: ‘ZKK_dt_travel’ Specifies the database table (ZKK_DT_TRAVEL) where the active (persisted) data of this entity is stored. createEnabled: true Allows the creation of new records in the entity. updateEnabled: true Allows modification of existing records. deleteEnabled: true Allows deletion of records. @odata.publish: true turns your CDS view into an OData service provider, making its data available for consumption via OData protocols. In short: This annotation block is used to define a CDS entity as a Business Object root node with full CRUD (Create, Read, Update, Delete) support and persistence in the custom table ZKK_DT_TRAVEL. Here I am creating the Metadata extension for the Basic view (ZI_KK_TRAVEL) for consuming it in the front end. @Metadata.layer: #CORE
@UI.headerInfo: {
typeName: ‘Travel’,
typeNamePlural: ‘Travel Details’
}
annotate entity zi_kk_travel
with
{
@UI.facet: [{
id: ‘Travel’,
purpose: #STANDARD,
position: 10,
label: ‘Travel Details ‘,
type: #IDENTIFICATION_REFERENCE
} ]
@UI.lineItem: [{ position: 10, label: ‘Travel Id’ }]
@UI.identification: [{ position: 10 }]
travel_id;
@UI.lineItem: [{ position: 20, label: ‘Email’ }]
@UI.identification: [{ position: 20 }]
user_email;
@UI.lineItem: [{ position: 30, label: ‘Travel Date’ }]
@UI.identification: [{ position: 30 }]
start_date;
@UI.lineItem: [{ position: 40, label: ‘Destination’ }]
@UI.identification: [{ position: 40 }]
destination;
@UI.lineItem: [{ position: 50, label: ‘Status’ }]
@UI.identification: [{ position: 50 }]
status;
@UI.lineItem: [{ position: 60, label: ‘Expense Type’ }]
@UI.identification: [{ position: 60 }]
expense_type;
@UI.lineItem: [{ position: 65, label: ‘Amount’ }]
@UI.identification: [{ position: 65 }]
total_cost;
@UI.lineItem: [{ position: 70, label: ‘Currency’ }]
@UI.identification: [{ position: 70 }]
curr_key;
@UI.lineItem: [{ position: 80, label: ‘Remarks for Expense’ }]
@UI.identification: [{ position: 80 }]
remarks;
@UI.lineItem: [{ position: 85, label: ‘Created By’ }]
@UI.identification: [{ position: 10 }]
created_by;
@UI.lineItem: [{ position: 90, label: ‘Created Date’ }]
@UI.identification: [{ position: 90 }]
created_on;
} Now activate the Basic view and the meta data extension, when it gets activated the Business Object get created implicitly. Now open the BOBX Transaction. We can search the business object in Home business objects or click on the below marked option.Provide the BO name which is our Basic View name (ZI_KK_TRAVEL) Now to Implement the validations expand the Node Elements Right Click on Validations will see the option to Create Validations Provide the Validation Name and Description and Class for implementing the validation logic. Double click on Class Name, and create a class which will be having the super class implicitly Now we have to provide Triggering conditions for the validations, so select the Node instance and tick on Create, Update, Delete and Check Save it and check and generate it. After this we have to go to eclipse and create a service definition on basic view (ZI_KK_TRAVEL) Now create a Service binding on top of Service definition and publish it When we preview it It will be navigated to the frontend while navigating it will ask for your SAP GUI Credentials to login.Now go to SE91 Transaction and create a message class Here, I am doing Validation for a Single status field only so i have passed only two messages Now I want to provide the Validations for Status field, so go to our class which we have created in Validations Implement our Custom login in this method. METHOD /bobf/if_frw_validation~execute.
CLEAR : et_failed_key, eo_message.
eo_message = /bobf/cl_frw_factory=>get_message( ).
DATA : lt_data TYPE ztikk_travel, ” Table type for header node
ls_msg TYPE symsg.
io_read->retrieve(
EXPORTING
iv_node = is_ctx-node_key
it_key = it_key
iv_fill_data = abap_true
it_requested_attributes = VALUE #(
( zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status )
) )
IMPORTING
et_data = lt_data
).
LOOP AT lt_data REFERENCE INTO DATA(ls_data).
IF ls_data->status IS INITIAL .
ls_msg-msgid = ‘ZKK_MSG_BOPF’.
ls_msg-msgno = ‘000’.
ls_msg-msgty = ‘E’.
eo_message->add_message(
is_msg = ls_msg ” Message that is to be added to the message object
iv_node = is_ctx-node_key ” Node to be used in the origin location
iv_key = ls_data->key ” Instance key to be used in the origin location
iv_attribute = zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status
” Attribute to be used in the origin location
).
INSERT VALUE #( key = ls_data->key ) INTO TABLE et_failed_key.
ENDIF.
DATA(lv_status) = to_upper( condense( ls_data->status ) ).
IF lv_status NA ‘ACCEPT’ OR lv_status NA ‘REJECT’.
ls_msg-msgid = ‘ZKK_MSG_BOPF’.
ls_msg-msgno = ‘001’.
ls_msg-msgty = ‘E’.
eo_message->add_message(
is_msg = ls_msg
iv_node = is_ctx-node_key
iv_key = ls_data->key
iv_attribute = zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status
).
INSERT VALUE #( key = ls_data->key ) INTO TABLE et_failed_key.
ENDIF.
ENDLOOP.
ENDMETHOD.Now go to service binding and preview it. Try to create a new record. Here I am keeping Status field as empty so it will throw error. Now Status should accept only ‘ACCEPT’ or ‘REJECT’. If I try to give any other value it will throw error again. If you give the correct value ‘Accept’ or ‘Reject’. ConclusionValidations in BOPF play a vital role in safeguarding data quality and enforcing business rules at the framework level. By defining validations directly on the business object nodes, we ensure that only consistent and compliant data enters the system, regardless of whether it originates from an SAP UI, an external interface, or a background process. This not only strengthens the reliability of the application but also reduces the need for redundant checks across different layers. In short, validations serve as a powerful and centralized mechanism to guarantee data integrity within the BOPF model. Read More Application Development and Automation Blog Posts articles
#SAP