Many a times third parties send their data in a file that will update for example a material master. For these scenarios we can use the standard IDoc to post data. IDoc gives us detailed messages as well a good logging feature and if something goes wrong we can reprocess them.
We will do this in the following steps:
- Read file
- Validate file
- Build IDoc
- Post IDoc
- Display log/IDoc status
Technically we will touch upon these things:
- Reading a file from PC
- Building an IDoc
- Posting an IDoc
- Building log messages
- Displaying log messages
- Tab strip
- Hotspot in an ALV
The file that comes in will be uploaded by the user from her local PC so we will give an option of F4 on file location. For this the code will be:
* F4
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
PERFORM open_file.
FORM open_file .
DATA:
l_t_file_table TYPE TABLE OF file_table,
l_s_file_table TYPE file_table.
CALL METHOD cl_gui_frontend_services=>file_open_dialog
CHANGING
file_table = l_t_file_table
rc = v_subrc
EXCEPTIONS
file_open_dialog_failed = 1
cntl_error = 2
error_no_gui = 3
not_supported_by_gui = 4
OTHERS = 5.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
READ TABLE l_t_file_table INTO l_s_file_table INDEX 1.
p_file = l_s_file_table-filename.
Before we start let’s open a log handle to a log so that any errors or success messages can be recorded.
FORM open_log .
* create a log
g_s_log-extnumber = 'Application Log in Subscreen'(001).
CALL FUNCTION 'BAL_LOG_CREATE'
EXPORTING
i_s_log = g_s_log
IMPORTING
e_log_handle = g_log_handle
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
INSERT g_log_handle INTO TABLE g_t_log_handle.
ENDFORM. " open_log
We will have procedure that will add the messages in the log, before calling the procedure a MESSAGE statement will set the system message variables.
FORM msg_add USING value(i_log_handle) TYPE balloghndl.
DATA:
l_s_msg TYPE bal_s_msg.
* define data of message for Application Log
l_s_msg-msgty = sy-msgty.
l_s_msg-msgid = sy-msgid.
l_s_msg-msgno = sy-msgno.
l_s_msg-msgv1 = sy-msgv1.
l_s_msg-msgv2 = sy-msgv2.
l_s_msg-msgv3 = sy-msgv3.
l_s_msg-msgv4 = sy-msgv4.
* add this message to log file
CALL FUNCTION 'BAL_LOG_MSG_ADD'
EXPORTING
i_log_handle = i_log_handle
i_s_msg = l_s_msg
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
ENDFORM. " msg_add
Read the file from PC.
FORM read_pc CHANGING l_subrc.
v_filename = p_file.
CALL METHOD cl_gui_frontend_services=>gui_upload
EXPORTING
filename = v_filename
CHANGING
data_tab = it_recs
EXCEPTIONS
file_open_error = 1
file_read_error = 2
no_batch = 3
gui_refuse_filetransfer = 4
invalid_type = 5
no_authority = 6
unknown_error = 7
bad_data_format = 8
header_not_allowed = 9
separator_not_allowed = 10
header_too_long = 11
unknown_dp_error = 12
access_denied = 13
dp_out_of_memory = 14
disk_full = 15
dp_timeout = 16
not_supported_by_gui = 17
error_no_gui = 18
OTHERS = 19.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO g_dummy.
PERFORM msg_add USING g_log_handle.
l_subrc = sy-subrc.
ELSE.
MESSAGE s100 INTO g_dummy. " File opened successfully
PERFORM msg_add USING g_log_handle.
PERFORM delimited_file.
ENDIF.
ENDFORM. "read_pc
After reading you might have your own checks on the file contents. e.g. material type is incorrect or if the mandatory values are not coming, etc. Once the checks are done it is time to build the IDoc. For each material in our case we will send one IDoc. The IDoc consists of a control part and data part. The control part tells SAP what type of IDoc is (definition) and what to do with the IDoc. The data part stores the data in different segments. Let’s see first how the control is made. The IDoc types will obviously be different for different scenarios.
* Control is of type EDIDC
edidc-mandt = sy-mandt. " Client
edidc-doctyp = 'MATMAS01'. " IDoc type
edidc-idoctp = 'MATMAS01'. " Basic Type
edidc-cimtyp = 'ZMATMAS01'. " Extension
edidc-direct = '2'. " Inbound
edidc-mestyp = 'MATMAS'. " Message Type
edidc-sndpor = 'DUMMY'. " Sender Port
edidc-sndprn = 'DUMMY'. " Sender partner
edidc-sndprt = 'LS'. " Partner type
Now that control record is done let’s see how the data part is done. The data is stored in the EDIDD structure. First move the relevant data in the structure for the relevant segment and then the segment is moved to the EDIDD structure. So this is done in two steps. We will see this in example of filling segment E1MARAM. The code will be something like this.
PERFORM fill_e1maram CHANGING s_e1maram.
PERFORM add_idoc_segment USING 'E1MARAM'
s_e1maram.
FORM fill_e1maram CHANGING ch_e1maram.
ch_e1maram-xxxxx = yyyyyyy.
.
.
.
ENDFORM.
FORM add_idoc_segment USING i_segnam i_sdata.
*
CLEAR s_edidd.
CHECK NOT i_sdata IS INITIAL.
s_edidd-mandt = sy-mandt.
s_edidd-segnam = i_segnam.
s_edidd-docnum = edidd-docnum.
s_edidd-sdata = i_sdata.
APPEND s_edidd TO t_edidd.
ENDFORM. "ADD_IDOC_SEGMENT
Once one material information is entered we should call the function module to create the IDoc. Along with posting we will also store the IDoc number and its status in out output structure S_OUTPUT.
FORM create_idoc.
CALL FUNCTION 'IDOC_WRITE_AND_START_INBOUND'
EXPORTING
i_edidc = edidc
IMPORTING
docnum = v_idoc_num
TABLES
i_edidd = t_edidd
EXCEPTIONS
idoc_not_saved = 1
OTHERS = 2
.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
PERFORM msg_add USING g_log_handle.
ELSE.
COMMIT WORK AND WAIT.
s_output-docnum = v_idoc_num.
* Check to see if IDOC posted successfully
SELECT SINGLE teds2~status descrp
INTO (s_output-status, s_output-descrp)
FROM edidc
JOIN teds2 ON teds2~status = edidc~status
WHERE docnum = v_idoc_num
AND langua = sy-langu.
* Put lights in the row
SELECT SINGLE stalight INTO s_output-traffic_light
FROM stacust
JOIN stalight ON stalight~statva = stacust~statva
WHERE status = s_output-status.
CASE s_output-traffic_light.
WHEN '1'.
* move icon_yellow_light to hugo.
s_output-traffic_light = 2.
WHEN '2'.
* move icon_green_light to hugo.
s_output-traffic_light = 3.
WHEN '3'.
* move icon_red_light to hugo.
s_output-traffic_light = 1.
ENDCASE.
ENDIF.
ENDFORM. "CREATE_IDOC
IDoc posting is done now lets show the user what happened. Over the course of opening the file and creating the IDoc we logging whatever happened so we want to show those messages as well as the IDoc posted. For this a tabbed screen appropriate so that user can have the details easy accessible. SAP has a very good demo of how to show logs in a tabbed subscreen in the program SBAL_DEMO_04_SUBSCREEN. That has been the basis of this program as well. So just looks at how the IDoc is displayed with a link to its application log. Our output structure is something like this:
The column CELL_TYPE can be used to define how a particular cell should behave in the ALV output. For IDoc number (DOCNUM) we want to be of type hotspot so the following will set it of that type.
* Make the IDoc of type link
l_s_salv_t_int4_column-columnname = 'DOCNUM'.
l_s_salv_t_int4_column-value = if_salv_c_cell_type=>hotspot.
APPEND l_s_salv_t_int4_column TO l_t_salv_t_int4_column.
s_output-cell_type = l_t_salv_t_int4_column.
MODIFY t_output FROM s_output TRANSPORTING cell_type
WHERE cell_type IS INITIAL.
Make the container and create the ALV table object.
* Create holder container
CREATE OBJECT l_r_cust_cont
EXPORTING =
container_name = 'G_CUST_CONTROL'
EXCEPTIONS
cntl_error = 1
cntl_system_error = 2
create_error = 3
lifetime_error = 4
lifetime_dynpro_dynpro_link = 5
OTHERS = 6
.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
*--------------------------------------------------------------------*
* Create AVL table
TRY.
cl_salv_table=>factory(
EXPORTING
r_container = l_r_cust_cont
IMPORTING
r_salv_table = l_r_table
CHANGING
t_table = t_output ).
CATCH cx_salv_msg. "#EC NO_HANDLER
ENDTRY.
Set the column CELL_TYPE as a cell definition column and we also plan to show the status with some traffic lights.
* Columns
DATA:
lr_columns TYPE REF TO cl_salv_columns_table,
lr_column TYPE REF TO cl_salv_column.
lr_columns = l_r_table->get_columns( ).
lr_columns->set_optimize( abap_true ).
TRY.
CALL METHOD lr_columns->set_exception_column
EXPORTING
value = 'TRAFFIC_LIGHT'
group = '2'
condensed = if_salv_c_bool_sap=>false.
CATCH cx_salv_data_error .
ENDTRY.
TRY.
CALL METHOD lr_columns->set_cell_type_column
EXPORTING
value = 'CELL_TYPE'.
CATCH cx_salv_data_error .
ENDTRY.
Define a event handler for the hotpot on IDoc number.
*--------------------------------------------------------------------*
* Events
DATA:
l_r_event_handler TYPE REF TO l_cl_event_handler,
l_r_events TYPE REF TO cl_salv_events_table.
l_r_events = l_r_table->get_event( ).
CREATE OBJECT l_r_event_handler.
SET HANDLER l_r_event_handler->on_link_click FOR l_r_events.
The handler for on_link_click event will be a local class.
CLASS l_cl_event_handler DEFINITION.
PUBLIC SECTION.
METHODS:
on_link_click FOR EVENT link_click OF cl_salv_events_table
IMPORTING row column.
PRIVATE SECTION.
METHODS:
handle_idoc_click IMPORTING i_row TYPE salv_de_row.
ENDCLASS. "l_cl_event_handler DEFINITION
In the method handle_idoc_click we will read the IDoc messages that are written in SAP’s application log. The demo SBAL_DEMO_05 has some good examples to save logs in the database and read from it. We will use the POPUP profile of log to show the message for given IDoc.
CLASS l_cl_event_handler IMPLEMENTATION.
METHOD on_link_click.
CASE column.
WHEN 'DOCNUM'.
handle_idoc_click( EXPORTING i_row = row ).
WHEN OTHERS.
ENDCASE.
ENDMETHOD. "on_click
METHOD handle_idoc_click.
DATA:
l_s_object TYPE bal_s_obj,
l_s_extnumber TYPE bal_s_extn,
l_s_log_filter TYPE bal_s_lfil,
l_t_log_header TYPE balhdr_t,
l_s_display_profile TYPE bal_s_prof,
l_s_log TYPE bal_s_log,
l_log_handle TYPE balloghndl,
l_t_log_handle TYPE bal_t_logh,
l_s_handle_filter TYPE bal_s_logh,
l_t_handle_filter TYPE TABLE OF bal_s_logh.
* Don't show programs own log
l_s_handle_filter-sign = 'E'.
l_s_handle_filter-option = 'EQ'.
l_s_handle_filter-low = g_log_handle.
APPEND l_s_handle_filter TO l_t_handle_filter.
l_s_log_filter-log_handle = l_t_handle_filter.
READ TABLE t_output INTO s_output INDEX i_row.
* create a filter with all relevant criteria:
l_s_object-sign = 'I'.
l_s_object-option = 'EQ'.
l_s_object-low = 'MATU'.
APPEND l_s_object TO l_s_log_filter-object.
l_s_extnumber-sign = 'I'.
l_s_extnumber-option = 'EQ'.
l_s_extnumber-low = s_output-docnum.
APPEND l_s_extnumber TO l_s_log_filter-extnumber.
* search on DB for these logs
CALL FUNCTION 'BAL_DB_SEARCH'
EXPORTING
i_s_log_filter = l_s_log_filter
IMPORTING
e_t_log_header = l_t_log_header
EXCEPTIONS
OTHERS = 0.
* load logs from DB
CALL FUNCTION 'BAL_DB_LOAD'
EXPORTING
i_t_log_header = l_t_log_header
EXCEPTIONS
OTHERS = 0.
* get a prepared profile
CALL FUNCTION 'BAL_DSP_PROFILE_POPUP_GET'
IMPORTING
e_s_display_profile = l_s_display_profile
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
l_s_display_profile-disvariant-handle = 'LOG'.
CALL FUNCTION 'BAL_DSP_LOG_DISPLAY'
EXPORTING
i_s_display_profile = l_s_display_profile
i_s_log_filter = l_s_log_filter
EXCEPTIONS
profile_inconsistent = 1
internal_error = 2
no_data_available = 3
no_authority = 4
OTHERS = 5.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE 'I' NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
ENDMETHOD. "handle_link_click
ENDCLASS. "l_cl_event_handler IMPLEMENTATION
The output will look something like this:
