ABAP BAPI Development: Creating and Using Business APIs

Category
ABAP-Statements
Published
Author
Johannes

BAPIs (Business Application Programming Interfaces) are standardized interfaces for accessing SAP business objects. They follow strict conventions and enable uniform integration.

BAPI Conventions

ConventionDescription
NamingBAPI_<OBJECT>_<ACTION>
RETURN parameterAlways BAPIRET2 or BAPIRET2_T
No COMMITCaller is responsible
No dialogsNo popups or messages
RFC-enabledRemote-enabled function module

BAPIRET2 Structure

FieldDescription
TYPES/E/W/I/A (Success/Error/Warning/Info/Abort)
IDMessage class
NUMBERMessage number
MESSAGEFull message text
MESSAGE_V1-V4Variables
LOG_NOLog number
LOG_MSG_NOLog message number

Examples

1. Call Standard BAPI

" BAPI to read a customer
DATA: ls_address TYPE bapicustomer_04,
lt_return TYPE TABLE OF bapiret2.
CALL FUNCTION 'BAPI_CUSTOMER_GETDETAIL2'
EXPORTING
customerno = '0000001000'
IMPORTING
customeraddress = ls_address
TABLES
return = lt_return.
" Error check
IF line_exists( lt_return[ type = 'E' ] ).
" Handle error
LOOP AT lt_return INTO DATA(ls_error) WHERE type = 'E'.
WRITE: / ls_error-message.
ENDLOOP.
ELSE.
" Success
WRITE: / ls_address-name, ls_address-city.
ENDIF.

2. BAPI with COMMIT

DATA: lt_return TYPE TABLE OF bapiret2.
" Create purchase order
CALL FUNCTION 'BAPI_PO_CREATE1'
EXPORTING
poheader = ls_header
poheaderx = ls_headerx
TABLES
poitem = lt_items
poitemx = lt_itemsx
return = lt_return.
" Error check
READ TABLE lt_return WITH KEY type = 'E' TRANSPORTING NO FIELDS.
IF sy-subrc <> 0.
" Success - perform COMMIT
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = abap_true. " Wait for commit
WRITE: / 'Purchase order created'.
ELSE.
" Error - ROLLBACK
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
" Output error messages
LOOP AT lt_return INTO DATA(ls_msg) WHERE type CA 'EAX'.
WRITE: / ls_msg-message.
ENDLOOP.
ENDIF.

3. Create Custom BAPI

" Function module: Z_BAPI_CUSTOMER_GETLIST
FUNCTION z_bapi_customer_getlist.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(IV_COUNTRY) TYPE LAND1 OPTIONAL
*" VALUE(IV_MAX_ROWS) TYPE I DEFAULT 100
*" EXPORTING
*" VALUE(ET_CUSTOMERS) TYPE ZTT_CUSTOMER_LIST
*" TABLES
*" RETURN STRUCTURE BAPIRET2
*"----------------------------------------------------------------------
" Input validation
IF iv_max_rows < 1 OR iv_max_rows > 10000.
append_return(
EXPORTING
iv_type = 'E'
iv_id = 'ZCUST'
iv_number = '001'
iv_v1 = CONV #( iv_max_rows )
CHANGING
ct_return = return[]
).
RETURN.
ENDIF.
" Read data
TRY.
SELECT kunnr, name1, ort01, land1
FROM kna1
WHERE land1 = @iv_country OR @iv_country IS INITIAL
INTO CORRESPONDING FIELDS OF TABLE @et_customers
UP TO @iv_max_rows ROWS.
IF sy-subrc <> 0.
append_return(
EXPORTING
iv_type = 'I'
iv_id = 'ZCUST'
iv_number = '002'
CHANGING
ct_return = return[]
).
ELSE.
append_return(
EXPORTING
iv_type = 'S'
iv_id = 'ZCUST'
iv_number = '003'
iv_v1 = CONV #( lines( et_customers ) )
CHANGING
ct_return = return[]
).
ENDIF.
CATCH cx_root INTO DATA(lx_error).
append_return(
EXPORTING
iv_type = 'E'
iv_id = 'ZCUST'
iv_number = '004'
iv_v1 = lx_error->get_text( )(50)
CHANGING
ct_return = return[]
).
ENDTRY.
ENDFUNCTION.
" Helper method to add RETURN entries
FORM append_return
USING
iv_type TYPE sy-msgty
iv_id TYPE sy-msgid
iv_number TYPE sy-msgno
iv_v1 TYPE clike OPTIONAL
iv_v2 TYPE clike OPTIONAL
iv_v3 TYPE clike OPTIONAL
iv_v4 TYPE clike OPTIONAL
CHANGING
ct_return TYPE bapiret2_t.
DATA: ls_return TYPE bapiret2,
lv_text TYPE string.
MESSAGE ID iv_id TYPE iv_type NUMBER iv_number
WITH iv_v1 iv_v2 iv_v3 iv_v4
INTO lv_text.
ls_return-type = iv_type.
ls_return-id = iv_id.
ls_return-number = iv_number.
ls_return-message = lv_text.
ls_return-message_v1 = iv_v1.
ls_return-message_v2 = iv_v2.
ls_return-message_v3 = iv_v3.
ls_return-message_v4 = iv_v4.
APPEND ls_return TO ct_return.
ENDFORM.

4. BAPI Wrapper Class

CLASS zcl_customer_bapi DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF ty_customer,
kunnr TYPE kunnr,
name1 TYPE name1_gp,
ort01 TYPE ort01_gp,
land1 TYPE land1_gp,
END OF ty_customer,
ty_customers TYPE STANDARD TABLE OF ty_customer WITH KEY kunnr.
METHODS: get_customer
IMPORTING iv_kunnr TYPE kunnr
RETURNING VALUE(rs_customer) TYPE ty_customer
RAISING zcx_bapi_error.
METHODS: get_customers_by_country
IMPORTING iv_country TYPE land1
RETURNING VALUE(rt_customers) TYPE ty_customers
RAISING zcx_bapi_error.
METHODS: create_customer
IMPORTING is_data TYPE ty_customer
RETURNING VALUE(rv_kunnr) TYPE kunnr
RAISING zcx_bapi_error.
PRIVATE SECTION.
METHODS: check_return
IMPORTING it_return TYPE bapiret2_t
RAISING zcx_bapi_error.
ENDCLASS.
CLASS zcl_customer_bapi IMPLEMENTATION.
METHOD get_customer.
DATA: ls_address TYPE bapicustomer_04,
lt_return TYPE TABLE OF bapiret2.
CALL FUNCTION 'BAPI_CUSTOMER_GETDETAIL2'
EXPORTING
customerno = iv_kunnr
IMPORTING
customeraddress = ls_address
TABLES
return = lt_return.
check_return( lt_return ).
rs_customer = VALUE #(
kunnr = iv_kunnr
name1 = ls_address-name
ort01 = ls_address-city
land1 = ls_address-country
).
ENDMETHOD.
METHOD get_customers_by_country.
DATA: lt_address TYPE TABLE OF bapicustomer_04,
lt_return TYPE TABLE OF bapiret2.
CALL FUNCTION 'BAPI_CUSTOMER_GETLIST'
TABLES
addressdata = lt_address
return = lt_return.
check_return( lt_return ).
rt_customers = VALUE #(
FOR ls_addr IN lt_address
WHERE ( country = iv_country OR iv_country IS INITIAL )
( kunnr = ls_addr-customer
name1 = ls_addr-name
ort01 = ls_addr-city
land1 = ls_addr-country )
).
ENDMETHOD.
METHOD create_customer.
DATA: lt_return TYPE TABLE OF bapiret2.
" BAPI for creation...
CALL FUNCTION 'BAPI_CUSTOMER_CREATE...'
" Parameters...
TABLES
return = lt_return.
check_return( lt_return ).
" On success COMMIT
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING wait = abap_true.
rv_kunnr = '...'. " New customer number
ENDMETHOD.
METHOD check_return.
" Check for errors in RETURN
LOOP AT it_return INTO DATA(ls_return)
WHERE type CA 'EAX'.
RAISE EXCEPTION TYPE zcx_bapi_error
EXPORTING
textid = zcx_bapi_error=>bapi_error
msgid = ls_return-id
msgno = ls_return-number
msgv1 = ls_return-message_v1
msgv2 = ls_return-message_v2
msgv3 = ls_return-message_v3
msgv4 = ls_return-message_v4.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
" Usage
TRY.
DATA(lo_customer) = NEW zcl_customer_bapi( ).
DATA(ls_customer) = lo_customer->get_customer( '0000001000' ).
WRITE: / ls_customer-name1, ls_customer-ort01.
CATCH zcx_bapi_error INTO DATA(lx_error).
WRITE: / 'Error:', lx_error->get_text( ).
ENDTRY.

5. BAPI for Mass Processing

FUNCTION z_bapi_orders_create_mass.
*"----------------------------------------------------------------------
*" IMPORTING
*" VALUE(IT_ORDERS) TYPE ZTT_ORDER_CREATE
*" EXPORTING
*" VALUE(ET_SUCCESS) TYPE ZTT_ORDER_NUMBERS
*" VALUE(ET_FAILED) TYPE ZTT_ORDER_FAILED
*" TABLES
*" RETURN STRUCTURE BAPIRET2
*"----------------------------------------------------------------------
DATA: lt_order_return TYPE TABLE OF bapiret2,
lv_order_no TYPE vbeln.
LOOP AT it_orders INTO DATA(ls_order).
CLEAR: lt_order_return, lv_order_no.
" Create single order
CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2'
EXPORTING
order_header_in = ls_order-header
IMPORTING
salesdocument = lv_order_no
TABLES
return = lt_order_return
order_items_in = ls_order-items
order_partners = ls_order-partners.
" Check
READ TABLE lt_order_return WITH KEY type = 'E' TRANSPORTING NO FIELDS.
IF sy-subrc <> 0 AND lv_order_no IS NOT INITIAL.
" Success
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING wait = abap_true.
APPEND VALUE #( order_no = lv_order_no ) TO et_success.
APPEND VALUE #(
type = 'S'
id = 'ZORD'
number = '001'
message = |Order { lv_order_no } created|
) TO return.
ELSE.
" Error
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
APPEND VALUE #(
index = sy-tabix
messages = lt_order_return
) TO et_failed.
APPEND LINES OF lt_order_return TO return.
ENDIF.
ENDLOOP.
" Summary
APPEND VALUE #(
type = 'I'
id = 'ZORD'
number = '002'
message = |{ lines( et_success ) } successful, { lines( et_failed ) } failed|
) TO return.
ENDFUNCTION.

6. BAPI Exception Class

CLASS zcx_bapi_error DEFINITION
INHERITING FROM cx_static_check
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES: if_t100_message.
CONSTANTS:
BEGIN OF bapi_error,
msgid TYPE symsgid VALUE 'ZBAPI',
msgno TYPE symsgno VALUE '001',
attr1 TYPE scx_attrname VALUE 'MSGV1',
attr2 TYPE scx_attrname VALUE 'MSGV2',
attr3 TYPE scx_attrname VALUE 'MSGV3',
attr4 TYPE scx_attrname VALUE 'MSGV4',
END OF bapi_error.
DATA: msgid TYPE sy-msgid,
msgno TYPE sy-msgno,
msgv1 TYPE sy-msgv1,
msgv2 TYPE sy-msgv2,
msgv3 TYPE sy-msgv3,
msgv4 TYPE sy-msgv4.
METHODS: constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
msgid TYPE sy-msgid OPTIONAL
msgno TYPE sy-msgno OPTIONAL
msgv1 TYPE sy-msgv1 OPTIONAL
msgv2 TYPE sy-msgv2 OPTIONAL
msgv3 TYPE sy-msgv3 OPTIONAL
msgv4 TYPE sy-msgv4 OPTIONAL.
CLASS-METHODS: from_bapiret2
IMPORTING is_return TYPE bapiret2
RETURNING VALUE(ro_error) TYPE REF TO zcx_bapi_error.
CLASS-METHODS: from_bapiret2_table
IMPORTING it_return TYPE bapiret2_t
RETURNING VALUE(ro_error) TYPE REF TO zcx_bapi_error.
ENDCLASS.
CLASS zcx_bapi_error IMPLEMENTATION.
METHOD constructor.
super->constructor( previous = previous ).
me->msgid = msgid.
me->msgno = msgno.
me->msgv1 = msgv1.
me->msgv2 = msgv2.
me->msgv3 = msgv3.
me->msgv4 = msgv4.
IF textid IS INITIAL.
if_t100_message~t100key = bapi_error.
ELSE.
if_t100_message~t100key = textid.
ENDIF.
ENDMETHOD.
METHOD from_bapiret2.
ro_error = NEW #(
msgid = is_return-id
msgno = is_return-number
msgv1 = is_return-message_v1
msgv2 = is_return-message_v2
msgv3 = is_return-message_v3
msgv4 = is_return-message_v4
).
ENDMETHOD.
METHOD from_bapiret2_table.
" Use first error message
LOOP AT it_return INTO DATA(ls_return) WHERE type CA 'EAX'.
ro_error = from_bapiret2( ls_return ).
RETURN.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

7. Call BAPI via RFC

DATA: lt_return TYPE TABLE OF bapiret2.
" RFC destination
DATA(lv_destination) = 'PRD_CLNT100'.
CALL FUNCTION 'BAPI_CUSTOMER_GETLIST'
DESTINATION lv_destination
TABLES
addressdata = lt_address
return = lt_return
EXCEPTIONS
communication_failure = 1 MESSAGE lv_message
system_failure = 2 MESSAGE lv_message
OTHERS = 3.
IF sy-subrc <> 0.
WRITE: / 'RFC error:', lv_message.
ELSE.
" Check BAPI return
check_return( lt_return ).
ENDIF.

8. BAPI with Update Task

FUNCTION z_bapi_document_save.
*"----------------------------------------------------------------------
*" IMPORTING
*" VALUE(IS_DOCUMENT) TYPE TY_DOCUMENT
*" TABLES
*" RETURN STRUCTURE BAPIRET2
*"----------------------------------------------------------------------
" Validation
perform_validation(
EXPORTING is_document = is_document
CHANGING ct_return = return[]
).
IF line_exists( return[ type = 'E' ] ).
RETURN.
ENDIF.
" Save in update task
CALL FUNCTION 'Z_UPDATE_DOCUMENT' IN UPDATE TASK
EXPORTING
is_document = is_document.
" Success message
APPEND VALUE bapiret2(
type = 'S'
id = 'ZDOC'
number = '001'
message = 'Document will be saved'
) TO return.
" COMMIT is performed by the caller!
ENDFUNCTION.

9. BAPI Helper Functions

CLASS zcl_bapi_helper DEFINITION.
PUBLIC SECTION.
" Check if RETURN contains errors
CLASS-METHODS: has_errors
IMPORTING it_return TYPE bapiret2_t
RETURNING VALUE(rv_error) TYPE abap_bool.
" Filter messages by type
CLASS-METHODS: get_messages_by_type
IMPORTING it_return TYPE bapiret2_t
iv_type TYPE sy-msgty
RETURNING VALUE(rt_messages) TYPE bapiret2_t.
" Convert RETURN to string
CLASS-METHODS: return_to_string
IMPORTING it_return TYPE bapiret2_t
RETURNING VALUE(rv_string) TYPE string.
" Add message
CLASS-METHODS: add_message
IMPORTING iv_type TYPE sy-msgty
iv_id TYPE sy-msgid
iv_number TYPE sy-msgno
iv_v1 TYPE clike OPTIONAL
iv_v2 TYPE clike OPTIONAL
iv_v3 TYPE clike OPTIONAL
iv_v4 TYPE clike OPTIONAL
CHANGING ct_return TYPE bapiret2_t.
ENDCLASS.
CLASS zcl_bapi_helper IMPLEMENTATION.
METHOD has_errors.
rv_error = xsdbool(
line_exists( it_return[ type = 'E' ] ) OR
line_exists( it_return[ type = 'A' ] ) OR
line_exists( it_return[ type = 'X' ] )
).
ENDMETHOD.
METHOD get_messages_by_type.
rt_messages = FILTER #( it_return WHERE type = iv_type ).
ENDMETHOD.
METHOD return_to_string.
LOOP AT it_return INTO DATA(ls_return).
IF rv_string IS NOT INITIAL.
rv_string = rv_string && cl_abap_char_utilities=>newline.
ENDIF.
rv_string = rv_string && |{ ls_return-type }: { ls_return-message }|.
ENDLOOP.
ENDMETHOD.
METHOD add_message.
DATA: ls_return TYPE bapiret2,
lv_text TYPE string.
MESSAGE ID iv_id TYPE iv_type NUMBER iv_number
WITH iv_v1 iv_v2 iv_v3 iv_v4
INTO lv_text.
ls_return = VALUE #(
type = iv_type
id = iv_id
number = iv_number
message = lv_text
message_v1 = iv_v1
message_v2 = iv_v2
message_v3 = iv_v3
message_v4 = iv_v4
).
APPEND ls_return TO ct_return.
ENDMETHOD.
ENDCLASS.
" Usage
DATA: lt_return TYPE bapiret2_t.
zcl_bapi_helper=>add_message(
EXPORTING
iv_type = 'S'
iv_id = 'ZMSG'
iv_number = '001'
iv_v1 = 'Success'
CHANGING
ct_return = lt_return
).
IF zcl_bapi_helper=>has_errors( lt_return ).
DATA(lv_errors) = zcl_bapi_helper=>return_to_string( lt_return ).
MESSAGE lv_errors TYPE 'E'.
ENDIF.

10. BAPI Explorer (BAPI)

Transaction: BAPI
1. Select Business Object (e.g., Customer)
2. Display methods (GetList, GetDetail, Create, etc.)
3. View documentation and parameters
4. Determine function module name
5. Test option via SE37

11. Important Standard BAPIs

" Customers
CALL FUNCTION 'BAPI_CUSTOMER_GETLIST'. " List customers
CALL FUNCTION 'BAPI_CUSTOMER_GETDETAIL2'. " Customer details
CALL FUNCTION 'BAPI_CUSTOMER_CREATEFROMDAT1'. " Create customer
" Vendors
CALL FUNCTION 'BAPI_VENDOR_GETLIST'. " List vendors
CALL FUNCTION 'BAPI_VENDOR_GETDETAIL'. " Vendor details
" Materials
CALL FUNCTION 'BAPI_MATERIAL_GETLIST'. " List materials
CALL FUNCTION 'BAPI_MATERIAL_GET_DETAIL'. " Material details
" Purchase orders
CALL FUNCTION 'BAPI_PO_CREATE1'. " Create purchase order
CALL FUNCTION 'BAPI_PO_CHANGE'. " Change purchase order
CALL FUNCTION 'BAPI_PO_GETDETAIL'. " Purchase order details
" Sales orders
CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2'. " Create sales order
CALL FUNCTION 'BAPI_SALESORDER_GETLIST'. " List sales orders
CALL FUNCTION 'BAPI_SALESORDER_CHANGE'. " Change sales order
" Postings
CALL FUNCTION 'BAPI_ACC_DOCUMENT_POST'. " Post FI document
" Transaction control
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'. " Commit
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'. " Rollback

BAPI Checklist

✓ Function module is RFC-enabled
✓ RETURN parameter of type BAPIRET2
✓ No COMMIT WORK in BAPI
✓ No MESSAGE statements
✓ No dialog calls (popups)
✓ Complete error handling
✓ Parameter documentation
✓ Authorization check implemented
✓ Input validation present
✓ Testable via SE37

Important Notes / Best Practice

  • COMMIT WORK never in BAPI – the caller decides.
  • Always fill RETURN with meaningful messages.
  • Mark as RFC-enabled for remote calls.
  • No MESSAGE statements – only populate RETURN.
  • Implement authorization check in BAPI.
  • Input validation before processing.
  • Wrapper classes for easy usage.
  • Use BAPI Explorer (transaction BAPI) for documentation.
  • Mass processing with individual COMMITs per record.
  • Combine with Exception Classes for OO integration.