Desarrollo de BAPIs en ABAP: Crear y usar Business APIs

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Las BAPIs (Business Application Programming Interfaces) son interfaces estandarizadas para acceder a objetos de negocio SAP. Siguen convenciones estrictas y permiten una integracion uniforme.

Convenciones BAPI

ConvencionDescripcion
NomenclaturaBAPI_<OBJETO>_<ACCION>
Parametro RETURNSiempre BAPIRET2 o BAPIRET2_T
Sin COMMITEl llamador es responsable
Sin dialogosSin popups ni mensajes
Habilitado RFCModulo de funcion remote-enabled

Estructura BAPIRET2

CampoDescripcion
TYPES/E/W/I/A (Success/Error/Warning/Info/Abort)
IDClase de mensaje
NUMBERNumero de mensaje
MESSAGETexto completo del mensaje
MESSAGE_V1-V4Variables
LOG_NONumero de log
LOG_MSG_NONumero de mensaje en log

Ejemplos

1. Llamar BAPI estandar

" BAPI para leer un cliente
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.
" Verificacion de errores
IF line_exists( lt_return[ type = 'E' ] ).
" Tratar error
LOOP AT lt_return INTO DATA(ls_error) WHERE type = 'E'.
WRITE: / ls_error-message.
ENDLOOP.
ELSE.
" Exito
WRITE: / ls_address-name, ls_address-city.
ENDIF.

2. BAPI con COMMIT

DATA: lt_return TYPE TABLE OF bapiret2.
" Crear pedido
CALL FUNCTION 'BAPI_PO_CREATE1'
EXPORTING
poheader = ls_header
poheaderx = ls_headerx
TABLES
poitem = lt_items
poitemx = lt_itemsx
return = lt_return.
" Verificacion de errores
READ TABLE lt_return WITH KEY type = 'E' TRANSPORTING NO FIELDS.
IF sy-subrc <> 0.
" Exito - ejecutar COMMIT
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = abap_true. " Esperar al Commit
WRITE: / 'Pedido creado'.
ELSE.
" Error - ROLLBACK
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
" Mostrar mensajes de error
LOOP AT lt_return INTO DATA(ls_msg) WHERE type CA 'EAX'.
WRITE: / ls_msg-message.
ENDLOOP.
ENDIF.

3. Crear BAPI propia

" Modulo de funcion: Z_BAPI_CUSTOMER_GETLIST
FUNCTION z_bapi_customer_getlist.
*"----------------------------------------------------------------------
*"*"Interfaz local:
*" 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
*"----------------------------------------------------------------------
" Validacion de entrada
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.
" Leer datos
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.
" Metodo auxiliar para agregar entradas RETURN
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. Clase Wrapper para BAPI

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 para crear...
CALL FUNCTION 'BAPI_CUSTOMER_CREATE...'
" Parametros...
TABLES
return = lt_return.
check_return( lt_return ).
" En exito COMMIT
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING wait = abap_true.
rv_kunnr = '...'. " Nuevo numero de cliente
ENDMETHOD.
METHOD check_return.
" Verificar errores en 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.
" Uso
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 para procesamiento masivo

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.
" Crear pedido individual
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.
" Verificar
READ TABLE lt_order_return WITH KEY type = 'E' TRANSPORTING NO FIELDS.
IF sy-subrc <> 0 AND lv_order_no IS NOT INITIAL.
" Exito
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 = |Pedido { lv_order_no } creado|
) 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.
" Resumen
APPEND VALUE #(
type = 'I'
id = 'ZORD'
number = '002'
message = |{ lines( et_success ) } exitosos, { lines( et_failed ) } fallidos|
) TO return.
ENDFUNCTION.

6. Clase de excepcion BAPI

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.
" Usar primer mensaje de error
LOOP AT it_return INTO DATA(ls_return) WHERE type CA 'EAX'.
ro_error = from_bapiret2( ls_return ).
RETURN.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

7. Llamar BAPI via RFC

DATA: lt_return TYPE TABLE OF bapiret2.
" Destino RFC
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: / 'Error RFC:', lv_message.
ELSE.
" Verificar retorno BAPI
check_return( lt_return ).
ENDIF.

8. BAPI con Update Task

FUNCTION z_bapi_document_save.
*"----------------------------------------------------------------------
*" IMPORTING
*" VALUE(IS_DOCUMENT) TYPE TY_DOCUMENT
*" TABLES
*" RETURN STRUCTURE BAPIRET2
*"----------------------------------------------------------------------
" Validacion
perform_validation(
EXPORTING is_document = is_document
CHANGING ct_return = return[]
).
IF line_exists( return[ type = 'E' ] ).
RETURN.
ENDIF.
" Guardar en Update Task
CALL FUNCTION 'Z_UPDATE_DOCUMENT' IN UPDATE TASK
EXPORTING
is_document = is_document.
" Mensaje de exito
APPEND VALUE bapiret2(
type = 'S'
id = 'ZDOC'
number = '001'
message = 'Documento sera guardado'
) TO return.
" COMMIT sera ejecutado por el llamador!
ENDFUNCTION.

9. Funciones auxiliares BAPI

CLASS zcl_bapi_helper DEFINITION.
PUBLIC SECTION.
" Verifica si RETURN contiene errores
CLASS-METHODS: has_errors
IMPORTING it_return TYPE bapiret2_t
RETURNING VALUE(rv_error) TYPE abap_bool.
" Filtra mensajes por tipo
CLASS-METHODS: get_messages_by_type
IMPORTING it_return TYPE bapiret2_t
iv_type TYPE sy-msgty
RETURNING VALUE(rt_messages) TYPE bapiret2_t.
" Convierte RETURN a String
CLASS-METHODS: return_to_string
IMPORTING it_return TYPE bapiret2_t
RETURNING VALUE(rv_string) TYPE string.
" Agrega mensaje
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.
" Uso
DATA: lt_return TYPE bapiret2_t.
zcl_bapi_helper=>add_message(
EXPORTING
iv_type = 'S'
iv_id = 'ZMSG'
iv_number = '001'
iv_v1 = 'Exito'
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)

Transaccion: BAPI
1. Seleccionar Business Object (ej. Customer)
2. Mostrar metodos (GetList, GetDetail, Create, etc.)
3. Ver documentacion y parametros
4. Determinar nombre del modulo de funcion
5. Posibilidad de test via SE37

11. BAPIs estandar importantes

" Clientes
CALL FUNCTION 'BAPI_CUSTOMER_GETLIST'. " Listar clientes
CALL FUNCTION 'BAPI_CUSTOMER_GETDETAIL2'. " Detalles de cliente
CALL FUNCTION 'BAPI_CUSTOMER_CREATEFROMDAT1'. " Crear cliente
" Proveedores
CALL FUNCTION 'BAPI_VENDOR_GETLIST'. " Listar proveedores
CALL FUNCTION 'BAPI_VENDOR_GETDETAIL'. " Detalles de proveedor
" Materiales
CALL FUNCTION 'BAPI_MATERIAL_GETLIST'. " Listar materiales
CALL FUNCTION 'BAPI_MATERIAL_GET_DETAIL'. " Detalles de material
" Pedidos de compra
CALL FUNCTION 'BAPI_PO_CREATE1'. " Crear pedido
CALL FUNCTION 'BAPI_PO_CHANGE'. " Modificar pedido
CALL FUNCTION 'BAPI_PO_GETDETAIL'. " Detalles de pedido
" Pedidos de venta
CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2'. " Crear pedido
CALL FUNCTION 'BAPI_SALESORDER_GETLIST'. " Listar pedidos
CALL FUNCTION 'BAPI_SALESORDER_CHANGE'. " Modificar pedido
" Contabilizaciones
CALL FUNCTION 'BAPI_ACC_DOCUMENT_POST'. " Contabilizar documento FI
" Control de transacciones
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'. " Commit
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'. " Rollback

Checklist BAPI

- Modulo de funcion es habilitado RFC
- Parametro RETURN del tipo BAPIRET2
- Sin COMMIT WORK en la BAPI
- Sin sentencias MESSAGE
- Sin llamadas de dialogo (Popups)
- Manejo de errores completo
- Documentacion de parametros
- Verificacion de autorizacion implementada
- Validacion de entrada presente
- Testeable via SE37

Notas importantes / Mejores practicas

  • COMMIT WORK nunca en la BAPI - el llamador decide.
  • RETURN siempre llenar con mensajes descriptivos.
  • Habilitado RFC marcar para llamadas remotas.
  • Sin MESSAGE statements - solo llenar RETURN.
  • Implementar verificacion de autorizacion en la BAPI.
  • Validacion de entrada antes del procesamiento.
  • Clases Wrapper para uso simplificado.
  • BAPI Explorer (transaccion BAPI) para documentacion.
  • Procesamiento masivo con COMMITs individuales por registro.
  • Combinar con Clases de excepcion para integracion OO.