CDS and BOPF Admin Data (create/change user and time) Determination using Annotations
Using the SAP Netweaver Innovation Package 7.51 sp1 you can implement new data models easily with CDS including full CRUD support, where change operations are managed by the BOPF framework. The BOPF Object is generated based on the Object Model annotations you put in the CDS View at activation.
This solution using SADL shows a BOPF adoptation/consumption, but also demonstrates how-to create your own consumer / framework asking SADL for CDS entity metainformation (Annotations). You have no more boundaries, your only blocker is your own creativity 🙂
Let say we have a DB table created already, and we would like to implement CRUD operations which could be extended with custom validation and determination of entry fields. One of the first thing we would achieve is to have the administrative fields maintained, like the last change time/user and the creator user/time. If you ever developed with BOPF previously, you would first try to include DDIC structure /BOBF/S_LIB_ADMIN_DATA
into your table maintaining ADMIN_DATA as group ID, plus adding a new determination reusing the well known class /BOBF/CL_LIB_D_ADMIN_DATA_TSM
. Unfortunately it dumps immediately, when you add this in eclipse ADT. (You can add it in eclipse only, since in GUI transactions the BOPF Object is locked against any change). This is due at the moment node category assigment “Before Save (Finalize)” cannot be added in eclipse, and might never added due the BOPF Object was generated from CDS, this is known only by the big CDS BOPF SADL Masters internally at SAP :).
You could create a custom determination class and fill the fields manually in every of your custom BO, but to support reusability and maitainability overall at your organization, you can create a reusable deterimination class inheriting from /bobf/cl_lib_d_superclass. This is exactly what I did, after I got some ideas from class /BOBF/CL_LIB_D_ADMIN_DATA_CDS
. This class dumped me for the same reason, and the annotations used within were not on the official ABAP CDS annotations list already. I read In the ABAP Documentation, that customers better not use custom annotations in ABAP CDS (which is supported in HANA), but to avoid conflict with partly existing SAP ABAP CDS Annotations I used my custom annotatiosn starting with Z.
Step 1 – Add fields to your DB table for admin data
Field | Data Element |
CREA_DATE_TIME | TIMESTAMPL |
CREA_UNAME | UNAME |
LCHG_DATE_TIME | TIMESTAMPL |
LCHG_UNAME | UNAME |
Step 2 – Create a CDS View
Eclipse (ADT): File – New – Other: Data Definition under Core Data Services. Here enter a name like ZCDS_I_MYDOCUMENT
@AbapCatalog.sqlViewName: 'ZV_ANABAPVIEWNAME' @AbapCatalog.compiler.compareFilter: true @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'My CDS View Name' @ObjectModel.modelCategory: #BUSINESS_OBJECT @ObjectModel.compositionRoot: true @ObjectModel.transactionalProcessingEnabled: true @ObjectModel.writeActivePersistence: 'ZT_YOURDBTABLE' @ObjectModel.createEnabled: true @ObjectModel.deleteEnabled: true @ObjectModel.updateEnabled: true @Search.searchable: true @OData.publish: true --Comment Out if you do not want an OData Service/Endpoint to be generated define view Zcds_I_Mydocument as select from zt_yourdbtable as MyDoc { @Search.defaultSearchElement: true key MyDoc.oneofyourfield, MyDoc.otherfield, @ZSemantics.systemDateTime.createdAt: true MyDoc.crea_date_time, @ZSemantics.user.createdBy: true MyDoc.crea_uname, @ZSemantics.systemDateTime.lastChangedAt: true MyDoc.lchg_date_time, @ZSemantics.user.lastChangedBy: true MyDoc.lchg_uname }
As you can see we have the same fields like in the structure /BOBF/S_LIB_ADMIN_DATA in the above example, but you can put any name for the admin fields. Here the key information is the DDIC type and the the annotation what you put before the CDS View field name. This is what we analyze in our custom determination class.
Step 3 – Create BOPF Determination class
"! <p class="shorttext synchronized" lang="en">Admin Data determination for CDS Views (Annotation Based)</p> CLASS zcl_admin_data_cds DEFINITION PUBLIC INHERITING FROM /bobf/cl_lib_d_superclass FINAL CREATE PUBLIC . PUBLIC SECTION. METHODS /bobf/if_frw_determination~execute REDEFINITION . PROTECTED SECTION. CONSTANTS co_annot_created_at TYPE string VALUE 'ZSEMANTICS.SYSTEMDATETIME.CREATEDAT' ##NO_TEXT. CONSTANTS co_annot_created_by TYPE string VALUE 'ZSEMANTICS.USER.CREATEDBY' ##NO_TEXT. CONSTANTS co_annot_changed_at TYPE string VALUE 'ZSEMANTICS.SYSTEMDATETIME.LASTCHANGEDAT' ##NO_TEXT. CONSTANTS co_annot_changed_by TYPE string VALUE 'ZSEMANTICS.USER.LASTCHANGEDBY' ##NO_TEXT. PRIVATE SECTION. ENDCLASS. CLASS zcl_admin_data_cds IMPLEMENTATION. METHOD /bobf/if_frw_determination~execute. DATA: node_entries TYPE REF TO data, node_entry TYPE REF TO data, timestamp_long TYPE timestampl. FIELD-SYMBOLS: <node_entry> TYPE any, <node_entries> TYPE INDEX TABLE, <node_entry_key> TYPE /bobf/conf_key, <created_by> TYPE uname, <created_at> TYPE timestampl, <changed_by> TYPE uname, <changed_at> TYPE timestampl. GET TIME STAMP FIELD timestamp_long. "After Create, After Update times are supported IF is_ctx-exectime <> /bobf/if_conf_c=>sc_time_after_create AND is_ctx-exectime <> /bobf/if_conf_c=>sc_time_after_modify. RAISE EXCEPTION TYPE /bobf/cx_lib EXPORTING textid = /bobf/cx_lib=>wrong_determination_time. ENDIF. "Retrieve node data /bobf/cl_frw_factory=>get_configuration( iv_bo_key = is_ctx-bo_key )->get_node( EXPORTING iv_node_key = is_ctx-node_key IMPORTING es_node = DATA(ls_node) ). "Ask SADL to provide CDS annotations for the view fields on which this BO node is based on TRY. cl_sadl_entity_factory=>get_instance( )->get_entity( iv_type = cl_sadl_entity_provider_cds=>gc_type iv_id = CONV #( ls_node-node_name ) )->get_annotations( IMPORTING et_element_annotations = DATA(elements_annotations) ). CATCH cx_sadl_static INTO DATA(lx_sadl_static). RAISE EXCEPTION TYPE /bobf/cx_conf_cds_link EXPORTING textid = /bobf/cx_conf_cds_link=>read_elements previous = lx_sadl_static mv_node = CONV #( ls_node-node_name ). ENDTRY. "Search for annotated view fields which should be updated LOOP AT elements_annotations ASSIGNING FIELD-SYMBOL(<element_annotations>). IF line_exists( <element_annotations>-annotations[ name = co_annot_created_at ] ). DATA(created_at_fieldname) = <element_annotations>-name. ELSEIF line_exists( <element_annotations>-annotations[ name = co_annot_created_by ] ). DATA(created_by_fieldname) = <element_annotations>-name. ELSEIF line_exists( <element_annotations>-annotations[ name = co_annot_changed_at ] ). DATA(changed_at_fieldname) = <element_annotations>-name. ELSEIF line_exists( <element_annotations>-annotations[ name = co_annot_changed_by ] ). DATA(changed_by_fieldname) = <element_annotations>-name. ENDIF. ENDLOOP. "Create work area and table based on BOPF node metadata used to iterate over the new/changed entries passed by the framework IF ls_node-data_table_type IS NOT INITIAL. CREATE DATA node_entries TYPE (ls_node-data_table_type). ELSE. CREATE DATA node_entries TYPE STANDARD TABLE OF (ls_node-data_type). ENDIF. ASSIGN node_entries->* TO <node_entries>. CREATE DATA node_entry TYPE (ls_node-data_type). ASSIGN node_entry->* TO <node_entry>. "Node Entry Key required for Update ASSIGN COMPONENT /bobf/if_conf_c=>sc_attribute_name_key OF STRUCTURE <node_entry> TO <node_entry_key>. "Bind fields of the work area with the annotated field name IF created_at_fieldname IS NOT INITIAL. ASSIGN node_entry->(created_at_fieldname) TO <created_at>. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /bobf/cx_lib EXPORTING textid = /bobf/cx_lib=>determination_error mv_fieldname = created_at_fieldname. ENDIF. ENDIF. IF created_by_fieldname IS NOT INITIAL. ASSIGN node_entry->(created_by_fieldname) TO <created_by>. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /bobf/cx_lib EXPORTING textid = /bobf/cx_lib=>determination_error mv_fieldname = created_by_fieldname. ENDIF. ENDIF. IF changed_at_fieldname IS NOT INITIAL. ASSIGN node_entry->(changed_at_fieldname) TO <changed_at>. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /bobf/cx_lib EXPORTING textid = /bobf/cx_lib=>determination_error mv_fieldname = changed_at_fieldname. ENDIF. ENDIF. IF changed_by_fieldname IS NOT INITIAL. ASSIGN node_entry->(changed_by_fieldname) TO <changed_by>. IF sy-subrc <> 0. RAISE EXCEPTION TYPE /bobf/cx_lib EXPORTING textid = /bobf/cx_lib=>determination_error mv_fieldname = changed_by_fieldname. ENDIF. ENDIF. "Retrieve node entries io_read->retrieve( EXPORTING iv_node = is_ctx-node_key it_key = it_key IMPORTING et_data = <node_entries> ). "Update administrative data fields LOOP AT <node_entries> INTO <node_entry>. "Changed by/at IF <created_at> IS ASSIGNED AND <created_at> IS INITIAL. <created_at> = timestamp_long. ENDIF. IF <created_by> IS ASSIGNED AND <created_by> IS INITIAL. <created_by> = sy-uname. ENDIF. "Changed by/at IF is_ctx-exectime = /bobf/if_conf_c=>sc_time_after_modify. IF <changed_at> IS ASSIGNED. <changed_at> = timestamp_long. ENDIF. IF <changed_by> IS ASSIGNED. <changed_by> = sy-uname. ENDIF. ENDIF. "Update Entry io_modify->update( EXPORTING iv_node = is_ctx-node_key iv_key = <node_entry_key> is_data = node_entry ). ENDLOOP. ENDMETHOD. ENDCLASS.
Step 4 – Add determination using the above class to your BO generated based on the CDS
Double click the generated BO in the ABAP Project Explorer in eclipse, and navigate to the node where you want to fill in the admin data. Here you need to go to the Dereminations, and create a new one like this.
You are ready. Do not be affriad, when Eclipse does not recognize the Z-Annotations maintained in the view, and colors it red.
Share this content: