Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
raghug
Active Contributor
SAP is in an interesting spot with ABAP. There are a lot of newer and better ways to things, but a lot of old things also work. Trying to get the benefits of the new ways while using existing business logic which was written the old way always throws up some fun.

In this particular case, I will show you the solution that I have come up with to handle the old style exceptions thrown by a function module call. This method is completely generic and should work with any function module call that throws classic exceptions.

Setup


The new exception class


Create a new exception class that you will use to "translate" these messages from one format to another. In this class, you have to add 5 new attributes to carry your Message Type and Message Variables.



If you are doing this with the Eclipse environment, this is what you would declare.
CLASS zcx_my_exception DEFINITION
PUBLIC
INHERITING FROM cx_static_check
FINAL
CREATE PUBLIC .

PUBLIC SECTION.

INTERFACES if_t100_message .

"Start of addition
DATA msgty TYPE symsgty .
DATA text1 TYPE sylisel .
DATA text2 TYPE sylisel .
DATA text3 TYPE sylisel .
DATA text4 TYPE sylisel .
"End of addition

METHODS constructor
IMPORTING
!textid LIKE if_t100_message=>t100key OPTIONAL
!previous LIKE previous OPTIONAL
!msgty TYPE symsgty OPTIONAL
!text1 TYPE sylisel OPTIONAL
!text2 TYPE sylisel OPTIONAL
!text3 TYPE sylisel OPTIONAL
!text4 TYPE sylisel OPTIONAL .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.

 

The calling Methods


In this code sample, I have the key pieces of code that you will need. Please don't pay attention to the fact that the variables and import - export parameters are not declared.

When declaring the class, all the methods have to be declared with the RAISING zcx_my_exception. This ensures that any exception that is raised is passed to the next level.
CLASS lcl DEFINITION.
PUBLIC SECTION.
METHODS get_master_data
RAISING zcx_my_exception.

METHODS call_function
RAISING zcx_my_exception.

METHODS call_bapi
RAISING zcx_my_exception.

ENDCLASS.


Standard Function Modules


First the standard function call. I have an additional method GET_MASTER_DATA( ) which is just an additional layer to show how exceptions can be handled at the highest level. For those of you who haven't used class based exceptions in ABAP before, this is to give you a glimpse of the power and simplicity of using this over the traditional method of passing and handling the exception at every level.
CLASS lcl IMPLEMENTATION.
METHOD get_master_data.

"Prep the material number, etc

me->call_function( ).

"Post call routine - Do something with data
me->call_bapi( ).

ENDMETHOD.

 
  METHOD call_function.

CALL FUNCTION 'MATERIAL_READ'
EXPORTING
schluessel = key_fields " Material master key fields
IMPORTING
matdaten = view_tab " Material master view
return = return
matper = matper
EXCEPTIONS
account_not_found = 1
batch_not_found = 2
forecast_not_found = 3
lock_on_account = 4
lock_on_material = 5
lock_on_plant = 6
lock_on_sales = 7
lock_on_sloc = 8
lock_on_batch = 9
lock_system_error = 10
material_not_found = 11
plant_not_found = 12
sales_not_found = 13
sloc_not_found = 14
slocnumber_not_found = 15
sloctype_not_found = 16
text_not_found = 17
unit_not_found = 18
invalid_mch1_matnr = 19
invalid_mtcom = 20
sa_material = 21
wv_material = 22
waart_error = 23
t134m_not_found = 24
error_message = 25
OTHERS = 26.

IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_my_exception
EXPORTING
textid = VALUE scx_t100key( msgid = sy-msgid
msgno = sy-msgno
attr1 = 'TEXT1'
attr2 = 'TEXT2'
attr3 = 'TEXT3'
attr4 = 'TEXT4' )
msgty = sy-msgty
text1 = CONV sylisel( sy-msgv1 )
text2 = CONV sylisel( sy-msgv2 )
text3 = CONV sylisel( sy-msgv3 )
text4 = CONV sylisel( sy-msgv4 ).
ENDIF.

ENDMETHOD.

A couple of points of note here. Always declare the extra catch all exception ERROR_MESSAGE (in this case = 25). This is a safety in case the person who programmed the function module raised an exception that was not declared. I have had crashes when I forgot to do this.

The other is the fact that I am also passing the MSGTY (Message Type). Even though the new class based exceptions are "Typeless", because you raise different ones in different circumstances, I sometimes find it useful to have this field around. This is a personal preference, because you can certainly make the argument that all exceptions out of a function module are errors.

Using a BAPI


is similar, but instead of the SYST fields, you will be using the data from the Bapi return table.
  METHOD call_bapi.
DATA:
bapiret_tab TYPE STANDARD TABLE OF bapiret2.

CALL FUNCTION 'BAPI_PRODORD_CREATE'
EXPORTING
orderdata = orderdata " Transfer Structure for Creating Production Orders
IMPORTING
return = bapiret_tab " Return Parameters
order_number = e_order_number. " Production order number

LOOP AT bapiret_tab INTO DATA(bapiret) WHERE type = 'E'.
RAISE EXCEPTION TYPE zcx_my_exception
EXPORTING
textid = VALUE scx_t100key( msgid = bapiret-id
msgno = bapiret-number
attr1 = 'TEXT1'
attr2 = 'TEXT2'
attr3 = 'TEXT3'
attr4 = 'TEXT4' )
msgty = bapiret-type
text1 = CONV sylisel( bapiret-message_v1 )
text2 = CONV sylisel( bapiret-message_v2 )
text3 = CONV sylisel( bapiret-message_v3 )
text4 = CONV sylisel( bapiret-message_v4 ).
ENDLOOP.

ENDMETHOD.

ENDCLASS.

The LOOP on the bapiret_tab is just one way of filtering the table. You can of course use the READ TABLE or table expressions (bapiret = bapiret_tab[ type = 'E' ] ) to achieve the same. This is where the use of the MSGTY gets interesting because a lot of BAPI's do send back the full gamut of Warnings and Success messages too. Some time in the future, I would like to experiment with making this RAISE a RESUMABLE one, and using that to log the Success and Warning messages.


Using the exceptions


This is the easy part! Just like any other TRY... CATCH.
  TRY.
DATA(obj) = NEW lcl( ).
obj->get_master_data( ).

CATCH zcx_my_exception INTO DATA(ocx_my).
" Do some clean up work
" ....

" Show the user the error message
MESSAGE ID ocx_my->if_t100_message~t100key-msgid
TYPE ocx_my->msgty
NUMBER ocx_my->if_t100_message~t100key-msgno
WITH ocx_my->text1
ocx_my->text2
ocx_my->text3
ocx_my->text4.
ENDTRY.

The only tricky part is how you get the MSGID and MSGNO from the exception object that has been caught - you have to go through the interface for accessing the T100 texts. So, that is a couple of layers deep.

Enjoy your errors!

Inspiration and References


This is in response to Mathew Billingham's question of a few years ago... Handling general errors via class based exceptions I just had a similar issue and just like him I realized that the exception object carries all the information that you need.

I have also had some inspiration from Horst Keller's post ABAP News for Release 7.50 – Converting Messages into Exceptions | SAP Blogs and also from Jörg Krause's Blog The hassle with function module calls – part 2 | SAP Blogs and the subsequent discussions.

 

 
7 Comments