Sunday, June 10, 2007

Post an IDoc from file and display its status

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:

  1. Read file
  2. Validate file
  3. Build IDoc
  4. Post IDoc
  5. Display log/IDoc status

Technically we will touch upon these things:

  1. Reading a file from PC
  2. Building an IDoc
  3. Posting an IDoc
  4. Building log messages
  5. Displaying log messages
  6. Tab strip
  7. 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:



image



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:



image

Tuesday, April 10, 2007

Create an IDoc message type via a custom BAPI

Swo1

Create object type and object

clip_image002

Super type is not mandatory

clip_image004

This creates object type

clip_image006

Now create key fields

clip_image008

clip_image010

clip_image012

Now create API method

clip_image014

Create API method is nothing but assigning function module(remote enabled). This function module should exists first, so first create function module (remote enabled) with a return parameter type BAPIRET2.

clip_image016

clip_image018

After creating the function module you should release it

clip_image020

Now in object add API method

clip_image022

clip_image024

Click on next step

clip_image026

Click on next step

clip_image028

Click yes will create method in object

clip_image030

Now put cursor on API method and go to menu Object type implement/release and Object type component implement/release, you should get tick beside API method(as released)

 clip_image032

Now change release status of object type and object to implemented and then to released

clip_image034

Go to Transaction: BDBG and enter object type and method

clip_image036

Now say create, here you are creating message type, etc.

clip_image038

clip_image040

This will list the IDoc created and the relevant function modules for it

clip_image042