The BAPIs BAPI_BUS1077_* from the function group 1077 are still very widely used in procedural code.
Nonetheless, it is the Object Oriented concept that supports Clean Code.
Therefore, in order to achieve clean ABAP code for EH&S Developments we can use the class CL_EHCSM_SPC_ACCESS instead of the 1077 BAPIs.
Simple Example:
Here I am going to demonstrate a rework of a procedural function module into a clean code method.
Here I am going to demonstrate a rework of a procedural function module into a clean code method.
Reading specification properties using the function module BAPI_BUS1077_GETDETAIL:
Procedural code:
SELECT matnr recnroot FROM estmj INTO CORRESPONDING FIELDS OF TABLE lt_estmj WHERE matnr = iv_matnr AND delflg = abap_false.
lv_lines = lines( lt_estmj ).
CASE lv_lines.
WHEN 0.
ev_return = '1'.
RETURN.
WHEN 1.
READ TABLE lt_estmj INTO ls_estmj INDEX 1.
WHEN OTHERS.
ev_return = '2'.
RETURN.
ENDCASE.
CLEAR: ls_subheader.
ls_subheader-record_no = ls_estmj-recnroot.
APPEND ls_subheader TO lt_subheader.
ls_propheader-subchacat = 'SAP_EHS_XXX_YYY'.
APPEND ls_propheader TO lt_propheader.
ls_propheader-subchacat = 'SAP_EHS_XXX_YYY'.
APPEND ls_propheader TO lt_propheader.
ls_propheader-subchacat = 'SAP_EHS_XXX_YYY'.
APPEND ls_propheader TO lt_propheader.
ls_propheader-subchacat = 'SAP_EHS_XXX_YYY'.
APPEND ls_propheader TO lt_propheader.
ls_propheader-subchacat = 'SAP_EHS_XXX_YYY'.
APPEND ls_propheader TO lt_propheader.
CALL FUNCTION 'BAPI_BUS1077_GETDETAIL'
EXPORTING
scenario = '01'
key_date = sy-datum
flg_header = 'X'
flg_matjoin = 'X'
flg_properties = 'X'
flg_prop_data = 'X'
flg_prop_details = 'X'
TABLES
return = lt_return
sub_header = lt_subheader
matjoin = lt_matjoin
prop_header = lt_propheader
prop_val = lt_propval
prop_data = lt_propdata
prop_usage = lt_propusage.
In the procedural code we find select statements, which should be avoided.
It is very common in the EH&S Development to be trying to read the specification to a certain material.
Recommendations:
- Avoid direct Selects
First try to find an existing function in your framework and only if nothing is available, write a select statement.
- A BAPI is better than a direct select.
- The method of a class is better than a BAPI.
zcl_ehs_hazard=>get_recn_by_matnr.
- Use INSERT VALUE instead of APPEND
Appended lines need a defined type. The INSERT VALUE is more flexible and can be used even if some structure type gets changed.
- DRY (DO NOT REPEAT YOURSEF)
In the above code we see that many properties were hard-coded and appended to the properties header table. In similar cases it is worth considering to move such logic to the configuration (you can use a z-table instead of the hard code). The advantage is that when business asks for an additional property to be evaluated, you can simple do this as a customizing activity, not affecting your code.
You can see in the clean code that the logic is capsuled in the method
zcl_ehs_hazard=>get_XYZ_vat.
- Use ABAP OO instead of procedural code
CL_EHCSM_SPC_ACCESS.
The method retrieve_val_char_data provides the option to filter the characteristics by usage. This means that if I want to evaluate a certain usage, for e.g. Validity Area Korea, I can filter before I get the data and don't have to worry afterwards.
- Prefer inline declaration always when it is possible, when not declare the variables close to their usage
With the OO Style we can use inline declarations for the import parameters (clean code)
- Avoid hard code or the so called magic words
You can see the hard coded scenario '01' in the procedural code. Usually, you can find the constants that you need in the interface. In the clean code, for example, we use if_ehcsm_spc_const_c=>gc_lockmode-no_lock.
Not everyone knows what '01' or 'N' is but everyone will understand 'no lock'.
- Call your variable and methods in a way that they can be pronounced
This makes your code more readable and the hand-over process more efficient.
Clean ABAP Code:
METHOD XYZ.
DATA(lo_access) = cl_ehcsm_spc_access=>get_instance( ).
DATA lt_key TYPE ehcsmt_spc_key.
DATA lt_usage_filter TYPE ehcsmt_spc_usage_filter.
INSERT VALUE #( recn = zcl_ehs_hazard=>get_recn_by_matnr( iv_matnr ) ) INTO TABLE lt_key .
INSERT VALUE #( rvlid = iv_country vaclid = zcl_ehs_hazard=>c_vaclid ) INTO TABLE lt_usage_filter.
lo_access->retrieve_val_char_data(
EXPORTING
it_spc_header_key = lt_key
it_vat = zcl_ehs_hazard=>get_XYZ_vat( )
it_spc_usg_filter = lt_usage_filter
iv_lock_mode = if_ehcsm_spc_const_c=>gc_lockmode-no_lock
IMPORTING
et_spc_val_char_data = DATA(lt_spc_char_data)
et_message = DATA(lt_message)
ev_severity = DATA(lv_severity)
).
DATA(lo_access) = cl_ehcsm_spc_access=>get_instance( ).
DATA lt_key TYPE ehcsmt_spc_key.
DATA lt_usage_filter TYPE ehcsmt_spc_usage_filter.
INSERT VALUE #( recn = zcl_ehs_hazard=>get_recn_by_matnr( iv_matnr ) ) INTO TABLE lt_key .
INSERT VALUE #( rvlid = iv_country vaclid = zcl_ehs_hazard=>c_vaclid ) INTO TABLE lt_usage_filter.
lo_access->retrieve_val_char_data(
EXPORTING
it_spc_header_key = lt_key
it_vat = zcl_ehs_hazard=>get_XYZ_vat( )
it_spc_usg_filter = lt_usage_filter
iv_lock_mode = if_ehcsm_spc_const_c=>gc_lockmode-no_lock
IMPORTING
et_spc_val_char_data = DATA(lt_spc_char_data)
et_message = DATA(lt_message)
ev_severity = DATA(lv_severity)
).
The most important is to use your framework and what it offers because it is tested!
Good luck!
Good luck!