top of page
Search
Writer's pictureWalf Sun

ETL Journey in SAP BODS

Updated: Aug 7

Combining ABAP and Python in SAP Data Services (SAP BODS) can involve using ABAP for extracting and transforming data within SAP ECC or S/4HANA, while Python can be used for additional processing or integration tasks outside of the SAP environment.


Scenario:

  • ABAP: Extracts data from SAP tables using a custom ABAP program or an SAP Data Services DataFlow with an ABAP data flow.

  • Python: Processes the extracted data, performs additional transformations, or integrates it with external systems.


Explanation:

  • ABAP Extraction: The ABAP program selects data from a custom SAP table (zcustomer_data) and sends it to SAP BODS via an RFC-enabled function module.

  • Python Processing: After SAP BODS processes the data and stores it in a CSV file, the Python script reads the data, applies additional transformations, and sends it to an external system via an API.


Integration Workflow:

  • SAP Data Services (SAP BODS): A job is designed in SAP BODS to execute the ABAP program via an ABAP data flow or to pull data via RFC/BAPI calls.

  • Python: Python script is executed after the SAP BODS job completes, processing the data further and integrating it with external systems.


How It Works Together:

  1. ABAP Report Execution: The process begins by executing the ABAP report zextract_customer_data, which extracts customer data from SAP.

  2. Data Export to BODS: The extracted data is then passed to Z_BODS_CUSTOMER_EXPORT, which prepares and sends this data to BODS.

  3. BODS Job Execution: The function Z_RFC_FUNCTION_TO_START_BODS_JOB triggers the BODS job to process the data.

  4. Python Processing: After the BODS job completes and outputs a CSV file, the Python script further processes this data and sends it to an external system.

  5. Error Handling: Throughout the process, each component has error handling mechanisms to ensure that failures are managed and logged appropriately.


Additional Considerations:

  • Configuration: Proper configuration of RFC destinations, table structures, and BODS job setup is essential for this program to work.

  • Security: Ensure that sensitive information (like passwords) is handled securely, especially in RFC connections.

  • Testing: Thorough testing in a development environment is recommended before deploying this program to production.


REPORT zextract_customer_data.

DATA: lt_customer_data TYPE TABLE OF zcustomer_data,

ls_customer_data TYPE zcustomer_data.


* Select customer data from SAP table with condition

SELECT * FROM zcustomer_data INTO TABLE lt_customer_data WHERE active = 'X'.


* Check if data is found before exporting

IF lt_customer_data IS NOT INITIAL.

* Pass the data to the Data Services job

CALL FUNCTION 'Z_BODS_CUSTOMER_EXPORT'

TABLES

customer_data = lt_customer_data

EXCEPTIONS

others = 1.


IF sy-subrc = 0.

WRITE: 'Data successfully exported to Data Services'.

ELSE.

WRITE: 'Error exporting data'.

ENDIF.

ELSE.

WRITE: 'No customer data found to export.'.

ENDIF.


Key Point:

  • Data Selection: The code selects active customer data from the zcustomer_data table.

  • Data Export: If data is found, it calls the Z_BODS_CUSTOMER_EXPORT function module to export the data to BODS.

  • Error Handling: It handles errors by checking sy-subrc and prints appropriate messages


Python Code for further Processing

Customer data is stored in a CSV file after extraction:


import pandas as pd

import requests


# Step 1: Load extracted customer data from CSV

try:

customer_data = pd.read_csv('customer_data.csv')

except FileNotFoundError:

print('CSV file not found.')

exit()


# Step 2: Data Transformation - Clean or enrich the data

customer_data['full_name'] = customer_data['first_name'].fillna('') + ' ' + customer_data['last_name'].fillna('')

customer_data = customer_data[customer_data['active'] == 'Y']


# Check if there is any data to send

if customer_data.empty:

print('No active customer data to send.')

else:

# Step 3: Integration - Send the processed data to an external API

api_url = 'https://example.com/api/customers'

headers = {'Content-Type': 'application/json'}

customer_data_json = customer_data.to_json(orient='records')


try:

response = requests.post(api_url, headers=headers, data=customer_data_json)

response.raise_for_status() # Raises an HTTPError for bad responses

print('Customer data successfully sent to external system.')

except requests.exceptions.RequestException as e:

print(f'Failed to send data: {e}')


Key Point

  • File Loading: The script reads customer data from a CSV file.

  • Data Transformation: It adds a full_name column and filters for active customers.

  • Data Export: If there is data, it sends the data to an external API using a POST request.

  • Error Handling: The script handles file loading errors and HTTP request errors.


FUNCTION Z_BODS_CUSTOMER_EXPORT'

*"----------------------------------------------------------------------

*"*"Local Interface:

*" TABLES

*" CUSTOMER_DATA STRUCTURE ZCUSTOMER_DATA

*"----------------------------------------------------------------------


DATA: lv_connection TYPE string,

lv_destination TYPE string,

lv_jobname TYPE string,

lv_status TYPE string,

lt_parameters TYPE TABLE OF sbdstpar,

ls_parameters TYPE sbdstpar,

lv_response TYPE char1.


TRY.

" Set destination (RFC Connection to BODS)

lv_destination = 'SAP_BODS_DEST'.


" Prepare the parameters for the BODS job

LOOP AT customer_data INTO DATA(ls_customer).

CLEAR ls_parameters.

ls_parameters-paramname = 'CUSTOMER_ID'.

ls_parameters-paramvalue = ls_customer-kunnr.

APPEND ls_parameters TO lt_parameters.

ENDLOOP.


" Call the BODS Job via RFC

CALL FUNCTION 'Z_RFC_FUNCTION_TO_START_BODS_JOB'

DESTINATION lv_destination

EXPORTING

jobname = lv_jobname

TABLES

parameters = lt_parameters

IMPORTING

jobstatus = lv_status

EXCEPTIONS

OTHERS = 1.


IF sy-subrc = 0 AND lv_status = 'SUCCESS'.

lv_response = 'X'.

ELSE.

lv_response = ' '.

RAISE EXCEPTION TYPE cx_bods_export_error.

ENDIF.


CATCH cx_bods_export_error INTO DATA(lx_error).

" Handle exceptions and errors

WRITE: 'Error occurred during BODS export: ', lx_error->get_text( ).

ENDTRY.


ENDFUNCTION.


Key points:

  • Parameter Preparation: It prepares parameters to pass to the BODS job.

  • RFC Call: Calls another function module to trigger the BODS job via an RFC connection.

  • Error Handling: Catches errors and raises a custom exception if the job fails.


ABAP Code Example to Create RFC Destination SAP_BODS_DEST


REPORT zcreate_rfc_destination.

DATA: lv_dest TYPE rfcdest VALUE 'SAP_BODS_DEST',

ls_options TYPE rfcoptions.


* Fill the RFC options structure

ls_options-rfctype = '3'. " ABAP connection type

ls_options-dest = lv_dest.

ls_options-destdesc = 'Connection to SAP Data Services'.

ls_options-host = 'bods_server_hostname'. " Hostname or IP of the BODS server

ls_options-sysnr = '00'. " System number of the target system (if applicable)

ls_options-client = '100'. " Client number

ls_options-user = 'BODS_USER'. " SAP username for the destination

ls_options-password = '********'. " SAP password for the user

ls_options-language = 'EN'. " Logon language


* Use the function module to create the RFC destination

CALL FUNCTION 'Z_RFC_DESTINATION_CREATE'

EXPORTING

options = ls_options

EXCEPTIONS

communication_failure = 1

system_failure = 2

OTHERS = 3.


IF sy-subrc = 0.

WRITE: 'RFC Destination SAP_BODS_DEST created successfully'.

ELSE.

WRITE: 'Failed to create RFC Destination SAP_BODS_DEST'.

ENDIF.


Key points;

  • Data Declaration: Variables lv_dest and ls_options are set up to store the RFC destination name and configuration settings.

  • Filling RFC Options Structure: The ls_options structure is filled with necessary connection details like type, destination name, description, host, system number, client, user, password, and language. These values are hardcoded, which works well for a fixed setup but may limit flexibility.

  • Calling the Function Module: The report uses the Z_RFC_DESTINATION_CREATE function module to create the RFC destination, passing the ls_options structure with the configuration details.

  • Exception Handling: Basic error handling is in place, checking for common issues like communication or system failures, but could be improved to handle more specific errors.

  • Output: After attempting to create the RFC destination, the code checks if it was successful and displays either a success or error message based on the result.


FUNCTION Z_RFC_DESTINATION_CREATE.

*"----------------------------------------------------------------------

*"*"Local Interface:

*" IMPORTING

*" VALUE(RFCDEST) TYPE RFCDES-RFCDEST

*" VALUE(DESCRIPTION) TYPE RFCDES-TXTLG

*" VALUE(CONNTYPE) TYPE RFCDES-CONNTYPE DEFAULT '3'

*" VALUE(ASHOST) TYPE RFCDES-ASHOST OPTIONAL

*" VALUE(SYSNR) TYPE RFCDES-SYSNR OPTIONAL

*" VALUE(CLIENT) TYPE RFCDES-CLIENT OPTIONAL

*" VALUE(USER) TYPE RFCDES-USER OPTIONAL

*" VALUE(PASSWORD) TYPE STRING OPTIONAL

*" VALUE(LANGUAGE) TYPE RFCDES-LANGU OPTIONAL

*" VALUE(PROGID) TYPE RFCDES-PROGID OPTIONAL

*" VALUE(GWHOST) TYPE RFCDES-GWHOST OPTIONAL

*" VALUE(GWSERV) TYPE RFCDES-GWSERV OPTIONAL

*" EXCEPTIONS

*" RFC_DESTINATION_EXISTS

*" RFC_DESTINATION_CREATION_FAILED

*"----------------------------------------------------------------------

DATA: ls_rfcdes TYPE rfcdes.


" Check if RFC destination already exists

SELECT SINGLE * INTO ls_rfcdes FROM rfcdes WHERE rfcdest = rfcdest.

IF sy-subrc = 0.

RAISE RFC_DESTINATION_EXISTS.

ENDIF.


" Populate the rfcdes structure with provided details

ls_rfcdes-rfcdest = rfcdest.

ls_rfcdes-txtlg = description.

ls_rfcdes-conntype = conntype.


IF conntype = '3'. " ABAP Connection

ls_rfcdes-ashost = ashost.

ls_rfcdes-sysnr = sysnr.

ls_rfcdes-client = client.

ls_rfcdes-user = user.

ls_rfcdes-langu = language.

" You might need to store the password securely elsewhere

ELSEIF conntype = 'T'. " TCP/IP Connection

ls_rfcdes-progid = progid.

ls_rfcdes-gwhost = gwhost.

ls_rfcdes-gwserv = gwserv.

ENDIF.


" Insert the new RFC destination into the database

INSERT INTO rfcdes VALUES ls_rfcdes.

IF sy-subrc <> 0.

RAISE RFC_DESTINATION_CREATION_FAILED.

ENDIF.


ENDFUNCTION.


Key points:

  • Check Existing RFC: It checks if the RFC destination already exists.

  • Populate Structure: Fills in the necessary details based on the connection type (ABAP or TCP/IP).

  • Insert RFC Destination: Inserts the new RFC destination into the database.


FUNCTION 'Z_RFC_FUNCTION_TO_START_BODS_JOB'

*"----------------------------------------------------------------------

*"*"Local Interface:

*" IMPORTING

*" VALUE(JOBNAME) TYPE STRING

*" TABLES

*" PARAMETERS STRUCTURE SBDSSTPAR

*" EXPORTING

*" VALUE(JOBSTATUS) TYPE STRING

*"----------------------------------------------------------------------


DATA: lv_rfc_dest TYPE rfcdes-rfcdest,

lv_bods_jobname TYPE string,

lt_bods_parameters TYPE TABLE OF sbdsstpar,

lv_execution_status TYPE string.


" Set the RFC destination to BODS

lv_rfc_dest = 'SAP_BODS_DEST'.


" Set the job name

lv_bods_jobname = jobname.


" Map the parameters

lt_bods_parameters[] = parameters[].


" Call the external BODS job via RFC destination

CALL FUNCTION 'Z_BODS_EXECUTE_JOB'

DESTINATION lv_rfc_dest

EXPORTING

jobname = lv_bods_jobname

TABLES

parameters = lt_bods_parameters

IMPORTING

jobstatus = lv_execution_status

EXCEPTIONS

others = 1.


" Return the execution status

IF sy-subrc = 0.

jobstatus = lv_execution_status.

ELSE.

jobstatus = 'FAILED'.

ENDIF.


ENDFUNCTION.


Key Points:


  • Exception Handling: Use specific exceptions for better error reporting and handling.

  • Logging: Consider adding logging or error messages for better traceability.

  • Configuration: Ensure that SAP_BODS_DEST and Z_BODS_EXECUTE_JOB are properly configured in your environment.


FUNCTION Z_BODS_EXECUTE_JOB.

*"----------------------------------------------------------------------

*"*"Local Interface:

*" IMPORTING

*" VALUE(JOBNAME) TYPE STRING

*" TABLES

*" PARAMETERS STRUCTURE SBDSSTPAR

*" EXPORTING

*" VALUE(JOBSTATUS) TYPE STRING

*" EXCEPTIONS

*" COMMUNICATION_FAILURE

*" SYSTEM_FAILURE

*" JOB_EXECUTION_FAILED

*"----------------------------------------------------------------------


DATA: lt_bods_return TYPE TABLE OF bapiret2,

lv_bods_message TYPE string,

lv_subrc TYPE sy-subrc.


" Initialize the BODS job call

CLEAR: jobstatus.


TRY.

" Replace the following RFC call with the actual BODS RFC or Web Service call

" Example: Calling an RFC to execute the BODS job

FUNCTION 'RFC_BODS_EXECUTE_JOB'

DESTINATION 'SAP_BODS_DEST'

EXPORTING

jobname = jobname

TABLES

parameters = parameters

IMPORTING

return_message = lv_bods_message

EXCEPTIONS

communication_failure = 1

system_failure = 2

others = 3.


IF sy-subrc = 0.

jobstatus = 'SUCCESS'.

ELSE.

jobstatus = 'FAILED'.

RAISE JOB_EXECUTION_FAILED.

ENDIF.


CATCH cx_rfc_communication_failure INTO DATA(lx_comm_failure).

WRITE: / 'Communication failure: ', lx_comm_failure->get_text( ).

RAISE COMMUNICATION_FAILURE.


CATCH cx_rfc_system_failure INTO DATA(lx_sys_failure).

WRITE: / 'System failure: ', lx_sys_failure->get_text( ).

RAISE SYSTEM_FAILURE.


CATCH cx_root INTO DATA(lx_root).

WRITE: / 'An unexpected error occurred: ', lx_root->get_text( ).

RAISE JOB_EXECUTION_FAILED.

ENDTRY.


ENDFUNCTION.


Key points:


  • Initialization:

  • Clears the jobstatus variable, preparing it for the job execution result.

  • RFC Call:

  • Calls an external BODS job via RFC (simulated by RFC_BODS_EXECUTE_JOB).

  • Passes the jobname and parameters to the RFC and imports a return message.

  • Error Handling:

  • sy-subrc Check: After the RFC call, it checks sy-subrc. If it's 0 (indicating success), jobstatus is set to 'SUCCESS'; otherwise, it sets jobstatus to 'FAILED' and raises JOB_EXECUTION_FAILED.

  • Exception Handling: Catches specific exceptions related to communication and system failures, logging the error and raising appropriate exceptions.

  • Final Status:

  • If no exceptions are raised, the function returns the jobstatus


15 views0 comments

Comments


Featured Blog Post

bottom of page