Push S/4 HANA Data Event to BTP Event Mesh in ABAP OO Mode
SAP has provided two native components “Enterprise Event Enablement” and “SAP Netweaver Addon for Event Enablement”. In the document, the component “SAP Netweaver Addon for Event Enablement” will be used.
I have get lots of helps from Zhang, Fiona’s blog post on how to push Data Event from S4 into Event Mesh. Whereas in most cases, the event message will be in deep structure rather than a flat structure. Please go through the rest of section for how to implement it via the framework.
This is not an end-to-end tutorial. The miss part can be found in Fiona’s blog post.
- SAP addon configuration
- Enhancement implementation
- Event Mesh configuration
- BTP service configuration
- BTP Event Mesh is working and Service Key has been generated.
- SAP Netweaver Addon for Event Mesh has been installed
- RFC Destination to Event Mesh and Token has been configured
if above steps have not been done, please check the details steps in Fiona’s blog post
In the example below, a purchase order create event is going to raised from S/4 hana. The event has header-item two layers. Different from all of the relevant blog posts using BOR object, the ABAP OO object will be used to raise the event. The effect would be the same. But just introduce a different way.
Create Custom ABAP Class Event
The class should be compatible with SAP business workflow. The implementation steps are exactly the same. If you are familar with the business workflow via ABAP OO, you can ignore this part.
The class should inherit interface IT_WORKFLOW
3. Constructor Method
Once the interface has been added, there will few methods inherited from the interface. Please add a custom constructor method
with following source code inside
METHOD constructor. DATA(lv_class_name) = cl_abap_classdescr=>get_class_name( me ). SPLIT lv_class_name+1 AT '=' INTO TABLE DATA(lt_class_name). me->_por-catid = lt_class_name[ 1 ]. me->_por-typeid = lt_class_name[ 2 ]. me->_por-instid = im_order. ME->_key = im_order. ENDMETHOD.
This is how the method looks like
4. Method BI_PERSISTENT~FIND_BY_LPOR
METHOD bi_persistent~find_by_lpor. DATA(lv_order) = CONV ebeln( lpor-instid(10) ). result = NEW zcl_purchase_order( lv_order ). ENDMETHOD.
5. Method BI_PERSISTENT~LPOR
METHOD bi_persistent~lpor. result = _por. ENDMETHOD.
6. Rest of Methods
For rest of methods, just activate them with empty source code.
7. Class Event
Create an event with name on_create. You can make any names making sense. There is no need add any parameters to the event. The event information will not be transfered via the parameter.
Create Virtual Message Type
The message type is a virtune entity used by the framework. Itself doen’t contain any structure information.
use tcode WE81 to create the message type
Activate the message type via BD50
Create Event Message Structure
This is the place define the event message structure. This is the structure of purchase order header. in order to make everything simple, it just contains order id and header text. Item field’s type is a table type so that it can be an array in the runtime.
This is the item structure. Remember, once create it, please create a table type referring this basic structure, which will be used in the header struture for item type.
now the structure will have two layers.
Link Change Document Object to Custom Event
The mechanism of ‘SAP Netweaver Addon for Event Enablement’ is triggered via the ‘Change Document Object’. here we link the change document object of purchase order creation to the custom event create just now.
The change document object for purchase order is: EINKBELEG
tip: please search table TCDOB for relevant change document object. Furthermore you can create your own custom change document object for your specific requirement.
Configure Replicate Object
This is the main configuration of the component ‘SAP Netweaver Addon for Event Enablement’.
Spro –> integration with other SAP component –> SAP Netweaver AddOn for Event enablement –> connection and Replication Object Custominzing
Please record the name of instance. Here BTP_EVENT_MESH has been used. It will be used later. For the RFC destination configuration, please check Fiona’s blog post.
2. Outbound Object
Fill the value as below
- Object name is PO_CREATE. Please record it since it will be used in the filter later.
- The SAP native extractor FM for Data Event is /ASADEV/ACI_GEN_VIEW_EXTRACTOR. However this guy can only handle view, which is a flat structure. We will create a custom one replacing it. Fill name ZACI_EVENT_EXTRACTOR here.
- Virtual Message type
- Select as disaplayed
- SAP EM
- The SAP native format FM for Data Event is /ASADEV/ACI_GEN_VIEWFRM_SAP_EM. Again the limitation of guy is it only work for flat structure. We will create a custom one replacing it. Fill name ZACI_EVENT_TO_JSON here.
3. Configure Header Attributes
ACI_VIEW should be the event message type created just now.
SAP_EM_TOPIC should be the topic configured in the BTP Event Mesh.
Link Custome Event and Virtual Message Type
Receiver Type is the virtual message type created just now.
Receiver FM is /ASADEV/ACI_EVENTS_TRIGGER
Do not forget the check the flag for linkage activation.
tip: the switch for ‘Linkage Activated’ can be used to switch on/off event connection.
Implement Relevant FM of Replicate Object
- Format FM zaci_event_to_json
The way to generate json string from ABAP structure is vary. You can develop own way as well. Just to make sure the parameters should be identical.
FUNCTION zaci_event_to_json . *"---------------------------------------------------------------------- *"*"Local Interface: *" IMPORTING *" VALUE(IR_DATA) TYPE DATA *" VALUE(IV_INSTANCE) TYPE /ASADEV/AMR_ARIBA_ARIBA_INST *" VALUE(IV_OBJECT) TYPE /ASADEV/AMR_ARIBA_ARIBA_OBJECT *" EXPORTING *" REFERENCE(ET_CSV_LINES) TYPE /ASADEV/AMR_TT_CSV_LINE *" REFERENCE(ET_RETURN) TYPE /ASADEV/ACI_TT_BAPIRET2 *"---------------------------------------------------------------------- DATA: lt_csv_lines TYPE /asadev/amr_tt_csv_line, " lt_csv_bundle TYPE /asadev/amr_tt_csv_line, " ls_amr_obj TYPE /asadev/amr_obj, " ls_csv_line TYPE LINE OF /asadev/amr_tt_csv_line. CONSTANTS: lc_attr_db_name TYPE /asadev/aci_def_attr VALUE 'ACI_VIEW'. FIELD-SYMBOLS: <ft_data> TYPE ANY TABLE. "get the data: ASSIGN ir_data->* TO <ft_data>. IF sy-subrc NE 0 OR <ft_data> IS INITIAL. RETURN. ENDIF. " convert data into json DATA(lv_json) = /ui2/cl_json=>serialize( data = <ft_data> pretty_name = /ui2/cl_json=>pretty_mode-low_case ). APPEND lv_json to lt_csv_lines. ls_amr_obj = /asadev/cl_aci_helper=>get_aci_object( iv_instance = iv_instance iv_object = iv_object ). *MOD-003 bundle as many csv_lines as customized into one cloud event LOOP AT lt_csv_lines INTO ls_csv_line. APPEND ls_csv_line TO lt_csv_bundle. IF lines( lt_csv_bundle ) EQ ls_amr_obj-item_lines. " now wrap the payload in the cloudevents header CALL FUNCTION '/ASADEV/ACI_SAP_EM_CLOUDEV_FM' EXPORTING it_csv_lines = lt_csv_bundle iv_instance = iv_instance iv_object = iv_object IMPORTING et_csv_lines = lt_csv_bundle et_return = et_return. APPEND LINES OF lt_csv_bundle TO et_csv_lines. IF ls_amr_obj-item_lines GT 0. DO ls_amr_obj-item_lines - 1 TIMES. APPEND INITIAL LINE TO et_csv_lines. ENDDO. ENDIF. CLEAR lt_csv_bundle. ENDIF. ENDLOOP. "Append the remaining lines IF lines( lt_csv_bundle ) GE 1. CALL FUNCTION '/ASADEV/ACI_SAP_EM_CLOUDEV_FM' EXPORTING it_csv_lines = lt_csv_bundle iv_instance = iv_instance iv_object = iv_object IMPORTING et_csv_lines = lt_csv_bundle et_return = et_return. APPEND LINES OF lt_csv_bundle TO et_csv_lines. IF ls_amr_obj-item_lines GT 0. DO ls_amr_obj-item_lines - 1 TIMES. APPEND INITIAL LINE TO et_csv_lines. ENDDO. ENDIF. CLEAR lt_csv_bundle. ENDIF. ENDFUNCTION.
2. Data Retrieving FM zaci_event_extractor
The save above. You can develop your own version as well. 🙂
FUNCTION zaci_event_extractor . *"---------------------------------------------------------------------- *"*"Local Interface: *" IMPORTING *" REFERENCE(IV_INSTANCE) TYPE /ASADEV/AMR_ARIBA_ARIBA_INST *" REFERENCE(IV_OBJECT) TYPE /ASADEV/AMR_ARIBA_ARIBA_OBJECT *" REFERENCE(IT_BDCP_LINES) TYPE /ASADEV/ACI_TT_BDCP *" EXPORTING *" REFERENCE(ET_RETURN) TYPE /ASADEV/ACI_TT_BAPIRET2 *" REFERENCE(ET_KEY_BDCP) TYPE /ASADEV/ACI_TT_BDCP_KEY *" REFERENCE(ET_TABLE_CONTENT) TYPE /ASADEV/ACI_TT_TABLE_CONTENT *" REFERENCE(ER_DATA) TYPE REF TO DATA *" EXCEPTIONS *" NOT_A_VALID_TABLE *"---------------------------------------------------------------------- DATA: lo_badi TYPE REF TO /asadev/aci_gen_extractor_badi, ct_table_content TYPE /asadev/aci_tt_table_content, lr_dataref_t TYPE REF TO data. * Get BAdI instance for extractor enhancements /asadev/cl_aci_badi_helpr=>get_extr_badi( EXPORTING iv_instance = iv_instance iv_object = iv_object CHANGING co_extractor_badi = lo_badi ct_return = et_return ). IF lo_badi IS BOUND. /asadev/cl_aci_badi_helpr=>call_badi_extr_post_exit( EXPORTING io_extractor_badi = lo_badi iv_instance = iv_instance iv_object = iv_object it_bdcp_lines = it_bdcp_lines CHANGING ct_table_content = ct_table_content ct_return = et_return cr_data = lr_dataref_t ). ENDIF. er_data = lr_dataref_t. ENDFUNCTION.
3. Implement BAdI /ASADEV/ACI_GEN_EXTR
The data extractor is generic and the concrete extracting logic should be embeded in a certain implementation with a specific filter.
create an implementation for purchase order
configure the filter for the PO implementation by using the bellowing information. The values should be from the above configuration sections. This will guarentee that only PO create event will be processed by the implmentation.
Implement method POST_EXIT. The logic depends on what kind of message structure defined. Here is my code
METHOD /asadev/aci_extrctpr_if~post_exit. DATA lr_dataref TYPE REF TO data. DATA(lv_order) = CONV ebeln( it_bdcp_lines[ 1 ]-tabkey+35 ). " Get PO number DATA: ls_data TYPE zpurchase_order. FIELD-SYMBOLS: <ls_purchase_order> type zpurchase_order, <lt_purchase_order> TYPE STANDARD TABLE. CREATE DATA lr_dataref TYPE TABLE OF zpurchase_order. ASSIGN lr_dataref->* TO <lt_purchase_order>. APPEND INITIAL LINE TO <lt_purchase_order> ASSIGNING <ls_purchase_order>. SELECT SINGLE * FROM ekko INTO @DATA(ls_ekko) WHERE ebeln EQ @lv_order. SELECT * FROM ekpo INTO TABLE @DATA(lt_ekpo) WHERE ebeln EQ @lv_order. <ls_purchase_order> = VALUE #( order_id = lv_order header_text = '' ). LOOP AT lt_ekpo ASSIGNING FIELD-SYMBOL(<ls_ekpo>). APPEND INITIAL LINE TO <ls_purchase_order>-items ASSIGNING FIELD-SYMBOL(<ls_item>). <ls_item>-item_number = <ls_ekpo>-ebelp. <ls_item>-item_text = <ls_ekpo>-txz01. <ls_item>-material = <ls_ekpo>-matnr. <ls_item>-quantity = <ls_ekpo>-menge. <ls_item>-uom = <ls_ekpo>-meins. ENDLOOP. cr_data = lr_dataref. ENDMETHOD.
1. Create a Purchase Order in the system
This is a dummy order created in the system with number 4500000749. It has three items whose quantities are 12, 24 and 56.
2. Event Received in the Event Mesh
The event does contain the order id and three items with correct their information.
- ABAP oo Event is working for ‘SAP Netweaver Addon for Event Enablement’
- Deep structure or multi layer can be fulfilled via custom FMs
During the investiagtion I have come up with a few questions. Hopefully someone can help me.
1. Comparison between two components. “SAP Netweaver Addon for Event Enablement” and “Enterprise Event Enablement”. What are pros and cons of them? Which one is more suitable for what kind of requirement?
2. For CPI connects with BTP Event Mesh, we have Webhook and AMQP. Can I know which way is recommended?