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:
ABAP Report Execution: The process begins by executing the ABAP report zextract_customer_data, which extracts customer data from SAP.
Data Export to BODS: The extracted data is then passed to Z_BODS_CUSTOMER_EXPORT, which prepares and sends this data to BODS.
BODS Job Execution: The function Z_RFC_FUNCTION_TO_START_BODS_JOB triggers the BODS job to process the data.
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.
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
Comments