CRM Rapid Applications – An Advanced Tutorial (Part 1)
Recently I started to play around with the CRM Rapid Applications (Rapid Applications – SAP Library). I wanted to use it to create a little application with some basic functionality. The test application should prefill some data by e.g. using some number range object. Furthermore, it should perform some data validation. As I had attended sitMUC (SAP Inside Track Munich 2014 – Community Events – SCN Wiki) just a few weeks ago where Daniel Ridder and Tobias Trapp gave a presentation on BRFplus (SAP Business Rules Management) I wanted to implement the validations using BRFplus rules.
As with most of the interesting SAP technologies there are basic tutorials available for both, CRM Rapid Applications (e.g. SAP CRM 7.0 EhP1 – Rapid Applications (Part 1) and SAP CRM 7.0 EhP1 – Rapid Applications (Part 2) by Tzanko Stefanov) and BRFplus. However, more advanced tutorials (like e.g. AXT Rapid Applications Currency/Quantity fields by Accenture Zenit) are hard to find.
Therefore, I took me quite a while to implement my sample application. I only succeeded by asking questions in the BRFplus forum and by extensive debugging in the CRM Rapid Application framework. Consequently, I decided to collect the knowledge gained in the process in this mini blog series in order to provide an advanced tutorial for CRM Rapid Applications as well as BRFplus. The first part of the blog series is focused on the Rapid Application part and the integration of BRFplus, the second part (CRM Rapid Applications – An Advanced Tutorial (Part 2)) will provide details on the design of the the BRFplus rules.
The Application Design
The applications shown in the existing rapid application tutorials are based on existing database tables. In contrast to that I will show how to create an application from scratch including the data base tables using only the rapid application functionality. For the sample application I defined the following requirements which are quite common in business applications. The application should be able to store:
- Header data and corresponding attributes
- The header should consist of an ID, a reference to a business parter, a creation and change date and user
- The attributes should be simple key value pairs.
In addition to that
- It should be possible to add but not to delete records
- The ID should be taken from a number range object
- The creation date and user and change date and user should be set automatically by the system
- The validity of the attributes should be check according to the following rules
- “Attribute 1” is mandatory, “Attribute 2” is optional.
- Allowed values for “Attribute 1” are “A”, “B” and “C”,
- Depending on the value of “Attribute 1” the following values are allowed for “Attribute 2”
Attribute 1 Attribute 2 A 1 – 99 B 100 – 299 C 300 – 999
According to these requirements the following table show valid records for the sample application:
|ID||Business Partner||Created At||Created By||Changed At||Changed By|
These requirements seemed simple enough for an introductory tutorial when I defined them in the first place. However, it turned out, that there are quite some difficulties hidden in them, both for CRM Rapid Applications and the BRFplus implementation.
Creating the Rapid Application
Rapid Applications are created using a wizard provided by the Rapid Application framework. The wizard is started by creating a new Rapid Application. As the example application for this blog should be based on database table select “Create from DB Table” to create the new application.
The Rapid Application wizard consists of the following steps:
1. Defining name and package for the application
2. Defining the database model
3. Defining the UI model
4. Optionally assigning the application to some UI profiles.
During each steps it is possible to generate and test the application. For this blog I created the application ZCD_RAPIDAPP_EX in the $TMP package.
Defining the Database Model
As mentioned above the goal of this blog is to show how to create a Rapid Application from scratch. Therefore, the next step is to define the database model. First the table to store the header data needs to be created. In order to create a new database table click on the “New” in the “DB Model” step of the wizard. This creates a new database table with an empty field list. Initially the new database table has a auto-generated name (ZCTAB000000N in the screen shot below). It is possible to change this name by clicking on “Details” and providing a custom name for the table (ZCD_RAE_HEADER in the screen shot below).
Although the field list initially appears to be empty the Rapid Application framework automatically generates some “technical” database fields. These field are CLIENT, RECORD_ID, PARENT_ID and OBJECT_ID, with CLIENT and RECORD_ID being the key fields of the database. The auto-generated fields are used by the framework to e.g. link entries in different tables.
The next step is to define the table fields required by the application. The following screen shot shows the table fields I generated for the table ZCD_RAE_HEADER. Note that the field ZZ_OBJECT_ID is defined as a logical key. The definition of a logical key is optional. However, in order to being able to define dependant tables a logical key is required in the superordinate table. Furthermore note, that some of the fields (e.g. ZZ_PARTNER or ZZ_CREATED_BY) have the field type “Application Reference”. Fields of type Application Reference are used to store references to other business objects like e.g. account or employee. Application references are used during the execution of the application to provide links in the UI to the respective objects. Finally, the fields available as search criteria need to be defined. This is done in the details view for each field.
To complete the database model for the application I created the dependent table ZCD_RAE_ATTRIBS to store the attributes. The following screen shot shows the details of this table. Note, that for the field ZZ_ATTRIBUTE I created a list of possible values. The values will be rendered as a drop down list when the application is executed.
Defining the UI Model
The next step is to define the UI model. The definition of the UI model consists of two steps, generating the views and adjusting the UI customizing. The first step is to automatically generate the UI model. This will create a search page, an overview page and a embedded search page. After the views have been generated the next step is to adjust the view. The only necessary adjustment for the example application is to remove the delete function from the overview page (cf. the following screen shot).
I didn’t add the the Rapid Application to a UI profile for this blog. Consequently, the final step is to save and generate the application. After the application has been generated it can be tested using the “Test” button. Note however, that not the complete functionality of the application is available in the test mode. For example, navigating to different business objects is not supported in the test mode.
The final step in defining the UI is to adjust the UI configuration. As with all SAP CRM applications this can either be done using the configuration mode or transaction BSP_WD_CMPWB_NEW. I change the standard configuration of the header details view as shown in the screen shot below. The business partner field is defined as mandatory, all other fields are display only. The other field should be automatically filled when a new record is created.
Extending the Rapid Application
In order to automatically populate the read only fields as well as to check the attributes according to the rules defined above it is necessary to extend the rapid application. In order to execute custom code during creating, changing and checking of a record the BAdI AXT_RT_TABLES_API is provided by SAP. The BAdI provides the methods ON_CREATE, ON_CHANGE, CHECK and QUERY. In this blog I will use the methods ON_CREATE, ON_CHANGE and CHECK to implement the requirements defined in the application design.
Automatically Populating Fields
The application design defines the following requirements:
- The Record ID should be taken from a number range object
- The creation date and user and change date and user should be set automatically by the system.
In order to achieve this I created the implementation ZCD_AXT_TABLES_RAE_HEADER_IMPL of the BAdI AXT_RT_TABLES_API. The execution of the BAdI is controlled by a filter on the table name. Therefore, I created the filter value ZCD_RAE_HEADER = TABLE_ID (see screen shot below).
The code snipped below shows the implementation of the ON_CREATE and ON_CHANGE methods of the BAdI implementation class. In the ON_CREATE method a new ZZ_OBJECT_ID is taken from a number range (lines 7 – 21). Besides that, only the fields ZZ_CREATED_BY, ZZ_CREATED_AT, ZZ_CHANGED_BY and ZZ_CHANGED_AT are initialized. The method GET_PARTNER_GUID_FOR_UNAME used in line 29 and 43 is a simple functional wrapper around the function module BP_CENTRALPERSON_GET.
METHOD if_ex_axt_rt_tables_api~on_create. DATA: header_data TYPE zcd_rae_header_work, partner_guid TYPE bu_partner_guid. header_data = cs_work_structure. IF header_data-zz_object_id IS INITIAL. CALL FUNCTION 'NUMBER_GET_NEXT' EXPORTING nr_range_nr = '01' object = 'ZCD_RAPID' IMPORTING number = header_data-zz_object_id EXCEPTIONS interval_not_found = 1 number_range_not_intern = 2 object_not_found = 3 quantity_is_0 = 4 quantity_is_not_1 = 5 interval_overflow = 6 buffer_overflow = 7 OTHERS = 8. IF sy-subrc <> 0. RETURN. ENDIF. ENDIF. IF header_data-zz_created_by IS INITIAL. partner_guid = me->get_partner_guid_from_uname( ). header_data-zz_created_by = partner_guid. header_data-zz_created_at = sy-datum. header_data-zz_changed_by = partner_guid. header_data-zz_changed_at = sy-datum. ENDIF. cs_work_structure = header_data. ENDMETHOD. METHOD if_ex_axt_rt_tables_api~on_change. DATA: header_data TYPE zcd_rae_header_work. header_data = cs_work_structure. header_data-zz_changed_by = me->get_partner_guid_from_uname( ). header_data-zz_changed_at = sy-datum. ENDMETHOD.
Issue Invoking the BAdI
The problem with the current status of the example application is that the BAdI implementation ZCD_AXT_TABLES_RAE_HEADER_IMPL will never get called. The reason is, that the Rapid Application framework checks if a logical key (the field ZZ_OBJECT_ID in the example application) before calling the BAdI. If the logical key is initial, an error message is raised an the BAdI is not called. Consequently it is impossible to implement the requirement to get the object ID from a number range object using only the BAdI AXT_RT_TABLES_API.
The only solution I found to circumvent this problem was to overwrite the DO_CREATE_ROOT method in the implementation class of the overview page ZCD_RAE_HEADER_OVP.
In general I don’t like the idea of having to enhance the overview page to enable the automatic creation of the object ID. Because the Rapid Application framework forces me to do this I have the logic to create an object is distributed across different parts of the application. However, I found a rather elegant solution for overwriting the method DO_CREATE_ROOT, which is shown in the following code snippet.
In order to create the root entity I simply invoke ON_CREATE method of the BAdI implementation in line 8. The loop in lines 17-22 then maps the resulting data to the creation parameters of the root entity. The root entity is then create in line 24.
METHOD do_create_root. DATA: header_data TYPE zcd_rae_header_work. DATA(bol_core) = cl_crm_bol_core=>get_instance( ). DATA(entity_factory) = bol_core->get_entity_factory( mv_main_entity ). DATA(creation_params) = entity_factory->get_parameter_table( ). NEW zcl_axt_tables_rae_header_impl( )->if_ex_axt_rt_tables_api~on_create( EXPORTING iv_object_id = '' iv_parent_id = '' iv_record_id = '' CHANGING cs_work_structure = header_data ). LOOP AT creation_params ASSIGNING FIELD-SYMBOL(<creation_param>). ASSIGN COMPONENT <creation_param>-name OF STRUCTURE header_data TO FIELD-SYMBOL(<comp>). IF <comp> IS ASSIGNED. <creation_param>-value = <comp>. ENDIF. ENDLOOP. DATA(bol_col) = bol_core->root_create( iv_object_name = mv_main_entity iv_create_param = creation_params iv_number = 1 ). eo_entity = bol_col->get_first( ). ENDMETHOD.
Checking the Attribute Validity
In order to check the attribute validity using BRFplus I created another implementation ZCD_AXT_TABLES_RAE_ATTRIB_IMPL of the BAdI AXT_RT_TABLES_API. For this implementation, however, the filter value ZCD_RAE_ATTRIBS = TABLE_ID is used (see screen shot below).
In the implementation class of the BAdI only the CHECK method is used. The code snippet below shows the implementation of the method. In order to check the attributes validity according to the defined rules, it is important to get all attributes of a header record. This is done by first getting the current attribute entity using the RECORD_ID and PARENT_ID in lines 8-13. From the current attribute entity the header entity is read in lines 19. In lines 24-34 all attribute entities related to the header entity are read and the attribute table for the invocation of the BRFplus function is prepared. The simplest way to find the correct entity names and relations is, as with other CRM applications, to use the BOL Model Browser in the transaction BSP_WD_CMPWB_NEW. Finally, the BRFplus function is invoked in line 36.
METHOD if_ex_axt_rt_tables_api~check. DATA: bol_key TYPE axts_ca_bol_key, attrib_properties TYPE zcd_rae_attribs_attr, attrib_properties_tab TYPE STANDARD TABLE OF zcd_rae_attribs_attr. DATA(bol_core) = cl_crm_bol_core=>get_instance( ). ASSIGN COMPONENT 'RECORD_ID' OF STRUCTURE is_work_structure TO FIELD-SYMBOL(<record_id>). ASSIGN COMPONENT 'PARENT_ID' OF STRUCTURE is_work_structure TO FIELD-SYMBOL(<parent_id>). bol_key-parent_id = <parent_id>. bol_key-record_id = <record_id>. DATA(current_entity) = bol_core->get_entity( EXPORTING iv_object_name = 'ZAET_CA_CTAB000014 ' iv_object_id = cl_crm_genil_container_tools=>build_object_id( is_object_key = bol_key ) ). CHECK current_entity IS BOUND. DATA(header_entity) = current_entity->get_parent( ). CHECK header_entity IS BOUND. DATA(attrib_entities) = header_entity->get_related_entities( iv_relation_name = 'ZAET_CA_TO_CTAB000014' ). DATA(iterator) = attrib_entities->get_iterator( ). DATA(attrib_entity) = iterator->get_first( ). WHILE attrib_entity IS BOUND. attrib_entity->get_properties( IMPORTING es_attributes = attrib_properties ). APPEND attrib_properties TO attrib_properties_tab. attrib_entity = iterator->get_next( ). ENDWHILE. ct_messages = me->execute_checks_using_brfplus( attrib_properties_tab ). ENDMETHOD.
The implementation of the second BAdI concludes the first part of the application. With only a few lines of code an mostly only configuration of the Rapid Application the result is a running example application that already fulfils most of the requirements stated in the application design. In the second part of the blog I’ll show how implement the validity checks for the attributes (ie. the implementation of the method EXECUTE_CHECKS_USING_BRFPLUS as well as the underlying BRFplus function.