Skip to Content
Technical Articles

How to create your own DMEE Tree Type? Part 2 of 2

Introduction

In first part of the Own DMEE Tree Type series I presented how to create own DMEE Tree Type and set up a new tree of own type.

Second part is concerned on triggering the own DME Tree via custom program and showing which standard functions can be used for that.

The definition created for usage of this blog will be an extraction of invoice data – header, lines and deliveries.

Each section of this post will show which part of the report is called and which DME functions are used. At the end whole report with all includes is presented so you can easily download it and implement in your system.

How does it work

To run DMEE basically 3 function modules are needed:

Function Module What it does
DMEE_START starts DME Engine and provides back sort fields
DMEE_PUT_ITEM adds item into opened DME Engine
DMEE_END closes DME and fills in the DME output table

There are however other useful functions that are available in standard.

When triggering DMEE start an output table needs to be passed ( parameter ‘file_output’) this is filled in internally in DMEE Engine every time DMEE_PUT_ITEM is used. Do not be afraid that you do not see any line in output table after that FM is called – it is stored as global data (it_output_file) of function pool DMEE2.

After calling DMEE_END function the output table is delivered with parameter ‘file_output’.

What else to consider

Besides these 3 function modules also others should be considered:

Function Module What it does
DMEE_NEW_FILE Evaluates if, based on the DMEE Tree key, a new file should be created. If returned parameter ‘xnew_file’ is set then current DME processing should be closed and new started
TAX_REP_SORT_DMEE_TABLE If output type is based on simple table (not hierarchical like invoice to line and to delivery) then this FM can be used to sort DMEE data before calling DMEE_PUT_ITEM. It makes sense to use it when all key fields are without sorting – then calling application should be provide sorted data already

Downloading / saving of file

After file is generated it can be displayed/ downloaded to local disc / saved on application server using:

Function Module What it does
DMEE_HANDLE_XML_DOC_PC Displays XML file generated by DMEE or lets saving it as local file
FI_PAYM_FILE_OPEN Opens dataset where logical file name will be set as FI_DME_CREATE_FILE (as introduced by OSS Note 1865372)
FI_PAYM_FILE_WRITE Saves DME file output to dataset
FI_FILE_CLOSE Closes dataset

In case of downloading a non-XML file there is no designated standard FM but it can be archived by simply looping over output lines and saving as TXT file.

Report

In the below-presented report data of invoices are extracted and displayed or downloaded.

Report output data in the same way as XML file:

Data will be presented as ALV Grid in hierarchical form using class CL_XX_ALV_TRANSFORMER and CL_XX_ALV_DISPLAY.

These classes are not widely known so I took this opportunity to show how to create hierarchical ALV Grid with their usage (normally you would use CL_SALV_TREE or CL_GUI_ALV_TREE).

If you do not have these classes in your system please implement OSS notes 2239078 (announcement note 2412784 – Shared Application and Business Services – Announcement) and all related ones (+ make cleanup afterwards as the implementation of these notes will most probably end up in syntax errors ).

If you cannot do that, then simply change the part of ALV coding to simple display of header data using SALV to see any output.

Report Structure

Report is written in the way to display XML/ flat or download it showing an ALV at the end.

Report consists of 4 includes:

Include What it contains
Z_DMEE_INVOICE_EXTRACT_LCL00 Top with class containing constants and commonly used data definitions
Z_DMEE_INVOICE_EXTRACT_SSC Selection screen
Z_DMEE_INVOICE_EXTRACT_LCL01 Local class LCL_SCREEN for processing of selection screen and saving its variables as class attributes
Z_DMEE_INVOICE_EXTRACT_LCL02 ALV processing routines in local class LCL_ALV_DISPLAY
Z_DMEE_INVOICE_EXTRACT_LCL03 Main report handler stored in class LCL_REPORT
REPORT z_dmee_invoice_extract.

"Custom DMEE Tree Type handling
INCLUDE z_dmee_invoice_extract_lcl00."Tools and data types
INCLUDE z_dmee_invoice_extract_ssc.  "Selection Screen
INCLUDE z_dmee_invoice_extract_lcl01."Screen processing class
INCLUDE z_dmee_invoice_extract_lcl02."ALV processing
INCLUDE z_dmee_invoice_extract_lcl03."Main report processing

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_vari.
  lcl_screen=>call_f4( lcl_tools=>c_f4_var ).

START-OF-SELECTION.
  lcl_screen=>get_instance( )->initialize( ).
  lcl_report=>get_instance( )->process( ).

Method process of class LCL_REPORT controls whole processing of report in submethods:

  METHOD process.

    select_data( ).   "Data selection
    generate_dmee( ). "Handling of all DMEE functions
    display_alv( ).   "Call LCL_ALV_DISPLAY class

  ENDMETHOD.

SELECT_DATA –> Selects invoices/ lines + deliveries

GENERATE_DMEE –> For data selected opens/ closes DME Engine Tool

DISPLAY_ALV –> calls method SETUP_ALV of class LCL_ALV_DISPLAY

In next part shortly methods will be presented and at the end whole report with example will be shown.

Data selection using SELECT_DATA method

It calls 3 methods which do actual data selection:

Method What it does
SELECT_INVOCES selects invoice headers from VBRK – values are saved in global attribute MT_INVOICE_HEADER
SELECT_INVOICE_LINES selects invoice lines from VBRP – values are saved in global attribute MT_INVOICE_LINES
SELECT_DELIVERIES selects delivery data from LIKP and LIPS – values are saved in global attribute MT_INVOICE_DELIVERIES

As stated in first part of own DMEE series the output of DME is generated out of 3 structures:

  • ZDMEE_INVOICE_HEADER represented in program by attribute MT_INVOICE_HEADER of class LCL_REPORT
  • ZDMEE_INVOICE_LINE represented in program by attribute MT_INVOICE_LINES of class LCL_REPORT
  • ZDMEE_DELIVERY_LINE represented in program by attribute MT_INVOICE_DELIVERIES of class LCL_REPORT

 

DMEE handling via GENERATE_DMEE method

This method processes the data selected in following way:

For each invoice header (MT_INVOICE_HEADER) evaluate if DMEE should be started using method START_DMEE_ENGINE.

Then add item to DMEE for each processed line item (MT_INVOICE_LINES) passing invoice header, invoice line and optionally delivery information. All that information is passed to method ADD_DMEE_ITEM where importing parameters are mapped to communication structure.

At the end, once all invoices have been processed, DME file is closed using CLOSE_ENGINE method.

Display (controlled by selection screen parameter) or download of file is handled in DOWNLOAD_FILE method.

METHOD generate_dmee.

    "for each invoice process DMEE
    LOOP AT mt_invoice_header ASSIGNING FIELD-SYMBOL(<l_s_invoice_header>).

      "start DME engine for invoice or keep it running if DME is already running
      start_dmee_engine( <l_s_invoice_header> ).

      "loop over each invoice item for currently processed invoice
      LOOP AT mt_invoice_lines ASSIGNING FIELD-SYMBOL(<l_s_invoice_line>) WHERE vbeln = <l_s_invoice_header>-vbeln.

        "loop over each delivery connected to invoice line or simply output invoice line if no delivery exists
        IF line_exists( mt_invoice_deliveries[ vbeln = <l_s_invoice_line>-vgbel posnr = <l_s_invoice_line>-vgpos ] ).
          LOOP AT mt_invoice_deliveries ASSIGNING FIELD-SYMBOL(<l_s_invoice_delivery>) WHERE vbeln = <l_s_invoice_line>-vgbel.
            "delivery-based-billing
            add_dmee_item( EXPORTING i_v_invoice_header = <l_s_invoice_header>
                                     i_v_invoice_line = <l_s_invoice_line>
                                     i_v_invoice_delivery = <l_s_invoice_delivery> ).
          ENDLOOP.
        ELSE.
          "order-based-billing
          add_dmee_item( EXPORTING i_v_invoice_header = <l_s_invoice_header>
                                   i_v_invoice_line = <l_s_invoice_line> ).
        ENDIF.

      ENDLOOP.
    ENDLOOP.

    "at the very end close DME engine
    close_engine( ).
    "and process display or download
    download_file( ).

  ENDMETHOD.

Starting DMEE creation via START_DMEE_ENGINE method

To start DME engine function module DMEE_START needs to be called. It controls opening an writing to one DMEE file.

Important part here is that, based on key fields definition in DMEE Tree, for one set of data multiple files can be created. This can be archived by settings in Levels + Sort Key Fields in DMEE Tree.

As a consequence it must be evaluated if DMEE was already started and if a new start is needed.

Therefore global attribute was introduced: MV_DMEE_FIRST_STARTED which is set once DMEE Engine is started for the first time.

A local parameter L_V_NEW_FILE_NEEDED returned by method CHECK_IF_NEW_FILE_NEEDED is populated if DMEE_NEW_FILE returns information that new file is needed.

When calling function DMEE_START information about Tree Type / Tree ID needs to be passed.

It is also required to pass the communication structure but there not all information needs to be passed (like in example where only invoice header data is provided).

Empty MT_DMEE_OUTPUT which holds output generated by DME needs to be provided as well.

Additionally global additional parameters available in global structure MS_DMEE_ADDITIONAL_DATA (representing ZDMEE_ADD_PARAMS from DMEE Tree Type settings).

Starting of DME also returns DMEE Tree sort fields which can be used for sorting of data passed to DMEE if it should happen outside of DMEE Engine.

METHOD start_dmee_engine.
    DATA: l_s_dmee_item        TYPE zdmee_invoice_data,
          l_t_dmee_sort_fields TYPE STANDARD TABLE OF dmee_tree_sort.

    l_s_dmee_item-zdmee_invoice_header = i_s_invoice.

    IF mv_dmee_first_started EQ abap_true.
      DATA(l_v_new_file_needed) = check_if_new_file_needed( l_s_dmee_item ).
    ENDIF.

    IF mv_dmee_first_started IS INITIAL
    OR l_v_new_file_needed IS NOT INITIAL.

      CALL FUNCTION 'DMEE_START'
        EXPORTING
          i_tree_type = lcl_tools=>c_dmee_tree_type
          i_tree_id   = m_o_screen->mv_dmee_tree
          item        = l_s_dmee_item
          param       = m_o_screen->ms_dmee_additional_data
*         uparam      = i_format_params
        TABLES
          file_output = mt_dmee_output
          sort_fields = l_t_dmee_sort_fields.

      IF mv_dmee_first_started IS INITIAL.
        mv_dmee_first_started = abap_true.
      ENDIF.
    ENDIF.

  ENDMETHOD.

As described before method CHECK_IF_NEW_FILE_NEEDED is calling DMEE_NEW_FILE function module so that DME Engine can evaluate if new file is needed or not.

  METHOD check_if_new_file_needed.

    CALL FUNCTION 'DMEE_NEW_FILE'
      EXPORTING
        item      = i_s_dmee_data
      CHANGING
        xnew_file = r_v_new_file_needed.

    IF r_v_new_file_needed IS NOT INITIAL.

      close_engine( ).
      download_file( ).

    ENDIF.

  ENDMETHOD.

If DMEE determined that new file is needed then currently processed file needs to be closed / downloaded/ saved to application server.

Add DMEE line via ADD_DMEE_ITEM method

After whole communication structure is filled with data ( at least invoice header / line item + optionally delivery information) it can be passed to DME Engine to be added to file output.

  METHOD add_dmee_item.
    DATA: l_s_dmee_item TYPE zdmee_invoice_data.

    l_s_dmee_item-zdmee_invoice_header = i_v_invoice_header.
    l_s_dmee_item-zdmee_invoice_line = i_v_invoice_line.
    l_s_dmee_item-zdmee_delivery_line = i_v_invoice_delivery.

    CALL FUNCTION 'DMEE_PUT_ITEM'
      EXPORTING
        item        = l_s_dmee_item
        param       = m_o_screen->ms_dmee_additional_data
*       uparam      = params
      TABLES
*       item_tab    = tab_item
        file_output = mt_dmee_output.

  ENDMETHOD.

Function Module DMEE_PUT_ITEM passed the data to DMEE internal table processing. Do not be afraid that after it is called table file_output is empty – it is meant to be so. Only after closing DMEE whole output data will be visible.

Close DMEE file via CLOSE_ENGINE method

At the moment when DMEE file should be finished (also if a new one should be opened) function module DMEE_END should be called. This is happening here. It outputs all possible errors which can be listed but generally if something is wrong in DMEE it results in two types or errors – at activation of DMEE or a dump in processing of it.

  METHOD close_engine.
    DATA: l_t_error_output   TYPE STANDARD TABLE OF fimsg.
    DATA: l_t_fpm_fields  TYPE STANDARD TABLE OF dmee_fpm_fields.

    CALL FUNCTION 'DMEE_END'
      TABLES
        file_output  = mt_dmee_output
        error_output = l_t_error_output
        fpm_fields   = l_t_fpm_fields.

  ENDMETHOD.

Download/ Display DMEE file via DOWNLOAD_FILE method

Preparation for download vary depending if output file is meant to be XML file or Flat File.

  METHOD download_file.

    CASE m_o_screen->mv_dmee_is_xml.
      WHEN abap_true.
        download_xml_file( ).
      WHEN abap_false.
        download_flat_file( ).
      WHEN OTHERS.
    ENDCASE.

  ENDMETHOD.

Therefore this method was split into two different ones:

Download / Display XML file

Handling of XML files is quite easy because there is standard function DMEE_HANDLE_XML_DOC_PC which needs to be called.

In our case method DOWNLOAD_XML_FILE checks if file should be displayed only (then no popup for file name comes up) or downloaded.

At the end XML_FILE_START_DOWNLOAD method is called where the FM mentioned above is called.

METHOD download_xml_file.
    CONSTANTS: l_c_xml TYPE string VALUE 'XML'.
    DATA:
      l_v_filename TYPE string,
      l_v_fullpath TYPE string,
      l_v_result   TYPE i,
      l_v_path     TYPE string.

    IF m_o_screen->mv_display_only EQ abap_true.

      xml_file_start_download( EXPORTING i_v_display_only = abap_true
                                         i_v_filename = l_v_filename ).
    ELSE.
      CALL METHOD cl_gui_frontend_services=>file_save_dialog
        EXPORTING
          default_extension = l_c_xml
        CHANGING
          filename          = l_v_filename
          path              = l_v_path
          fullpath          = l_v_fullpath
          user_action       = l_v_result.

      IF l_v_result IS INITIAL.

        xml_file_start_download( EXPORTING i_v_display_only = abap_false
                                           i_v_filename = l_v_filename ).

      ENDIF.
    ENDIF.

  ENDMETHOD.

Only a file name needs to be provided (in case it should be displayed only an empty variable can be passed) + additionally information if it should be displayed only  (parameter I_DISPLAY)  or saved (parameter I_SAVE). Combining display and save will enable saving from XML display.

  METHOD xml_file_start_download.

    CALL FUNCTION 'DMEE_HANDLE_XML_DOC_PC'
      EXPORTING
        i_filename = i_v_filename
        i_save     = COND xfeld( WHEN i_v_display_only EQ abap_true THEN space
                                 ELSE abap_true )
        i_display  = i_v_display_only.

  ENDMETHOD.

Download / Display Flat file

Flat files do not have dedicated standard DMEE function module and are handled as normal TXT files.

The download just needs to build a table of string type and move the DMEE output there. In this example I have programmed it to be an TXT file but it can easily be a CSV or any other as well.

METHOD download_flat_file.
    CONSTANTS c_file_txt TYPE string VALUE 'TXT'.

    DATA:
      l_v_filename TYPE string,
      l_v_path     TYPE string,
      l_v_fullpath TYPE string,
      l_v_result   TYPE i.
    DATA: l_v_codepage TYPE cpcodepage.
    DATA: l_t_file_data TYPE STANDARD TABLE OF string WITH EMPTY KEY.

    IF m_o_screen->mv_display_only IS NOT INITIAL.

      "display only
      CALL FUNCTION 'POPUP_WITH_TABLE_DISPLAY'
        EXPORTING
          endpos_col   = 30
          endpos_row   = lines( mt_dmee_output )
          startpos_col = 10
          startpos_row = 1
          titletext    = text-004
*        IMPORTING
*         choise       = l_v_choice
        TABLES
          valuetab     = mt_dmee_output
        EXCEPTIONS
          break_off    = 1
          OTHERS       = 2.

    ELSE.
      "download
      LOOP AT mt_dmee_output ASSIGNING FIELD-SYMBOL(<l_s_dme_line>).

        APPEND <l_s_dme_line>-line TO l_t_file_data.

      ENDLOOP.

      "get default encoding by language
      CALL FUNCTION 'NLS_GET_FRONTEND_CP'
        EXPORTING
          langu                 = CONV spras( sy-langu ) ##OPERATOR
*         FETYPE                = 'MS'
        IMPORTING
          frontend_codepage     = l_v_codepage
        EXCEPTIONS
          illegal_syst_codepage = 1
          no_frontend_cp_found  = 2
          internal_or_db_error  = 3
          OTHERS                = 4.
      IF sy-subrc NE 0.
        l_v_codepage = '4110'.
      ENDIF.
      CONDENSE l_v_codepage NO-GAPS.

      CALL METHOD cl_gui_frontend_services=>file_save_dialog
        EXPORTING
          default_extension = CONV string( c_file_txt )
        CHANGING
          filename          = l_v_filename
          path              = l_v_path
          fullpath          = l_v_fullpath
          user_action       = l_v_result.

      IF l_v_result IS INITIAL.

        CALL FUNCTION 'GUI_DOWNLOAD'
          EXPORTING
            filename = l_v_filename
            codepage = CONV abap_encoding( l_v_codepage )
          TABLES
            data_tab = l_t_file_data
          EXCEPTIONS
            OTHERS   = 1.
        IF sy-subrc NE 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
          WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ELSE.
          MESSAGE i427(fr) WITH l_v_filename.
        ENDIF.
      ENDIF.

    ENDIF.
  ENDMETHOD.

This part concludes the DMEE handling. In next part output is shortly presented as well as whole report in a ready-to-download format.

ALV Creation using CL_XX_ALV_TRANSFORMER

Data selected by report will be presented in form of a hierarchical ALV Grid.

Class LCL_ALV_DISPLAY is called from main report passing the data:

  METHOD display_alv.

    NEW lcl_alv_display( i_t_invoice_headers = mt_invoice_header
                         i_t_invoice_lines = mt_invoice_lines
                         i_t_invoice_deliveries = mt_invoice_deliveries )->setup_alv( ).

  ENDMETHOD.

Method SETUP_ALV is split into 2 submethods where SETUP_ALV_NODES creates the DMEE-like output and the DISPLAY simply displays it.

  METHOD setup_alv.

    setup_alv_nodes( ).
    display( ).

  ENDMETHOD.

DMEE-like ALV output: Nodes setup

With setup of nodes we want to archive DMEE-like display of data to be able to easily compare the nodes of XML file with ALV output.

This can be archived using methods of interface IF_XX_ALV_TRANSFORMER or via class CL_XX_ALV_TRANSFORMER.

Method ADD_VIEW controls each view which can be setup as:

  • Header
  • Tree Folder
  • Simple tree node

The expected output is:

For each invoice a folder needs to be created.

If invoice line has a delivery assigned it must be created as a folder that is nested under its invoice folder. If invoice line has no delivery assigned then it must be output as simple tree node.

Each delivery assigned to invoice line must be presented as simple tree node.

Controlling if node is a folder or tree is done using parameters:

  • IV_IS_TREE_FOLDER
  • IV_IS_TREE

Setting both at the same time will work out in an error so choose only one.

Each node/ folder will have own internal table showing data which is stored in it.

Transforming the concept from paper to code gives following output:

 METHOD setup_alv_nodes.
    DATA: l_v_header_title TYPE sytitle.
    DATA: l_v_title        TYPE char30.

    l_v_header_title = text-001.

    "inform user what is happening now
    CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
      EXPORTING
        text = text-i01.

    TRY.
        "add single invoice ALV header
        mo_alv_transformer->add_alv_view( iv_is_header = abap_true
                                          iv_header_title  = l_v_header_title ).

        "Folder for each invoice
        LOOP AT mt_headers ASSIGNING FIELD-SYMBOL(<l_s_invoice_header>).
          WRITE <l_s_invoice_header>-vbeln TO l_v_title.

          DATA(l_t_headers_alv) = VALUE lcl_tools=>tt_invoice_header_alv( ( <l_s_invoice_header> ) ).

          mo_alv_transformer->add_alv_view( iv_structure_name = lcl_tools=>c_structure_header
                                            iv_default_variant = m_o_screen->mv_layout
                                            it_outtab   = l_t_headers_alv
                                            iv_is_tree_folder = abap_true
                                            iv_tree_folder_title = l_v_title ).

          LOOP AT mt_lines ASSIGNING FIELD-SYMBOL(<l_s_invoice_line>) WHERE vbeln = <l_s_invoice_header>-vbeln.

            DATA(l_t_lines_alv) = VALUE lcl_tools=>tt_invoice_lines_alv( ( <l_s_invoice_line> ) ).

            l_v_title = |{ <l_s_invoice_line>-posnr ALPHA = OUT }|.

            "If delivery exists for a line - make a folder
            "If not - just output items as subnodes
            IF line_exists( mt_deliveries[ vbeln = <l_s_invoice_line>-vgbel ] ).
              DATA(l_v_delivery_exists) = abap_true.
            ELSE.
              CLEAR l_v_delivery_exists.
            ENDIF.

            mo_alv_transformer->add_alv_view( iv_structure_name = lcl_tools=>c_structure_line
                                              it_outtab = l_t_lines_alv
                                              iv_is_tree_folder = COND xfeld( WHEN l_v_delivery_exists EQ abap_true THEN abap_true
                                                                              ELSE abap_false )
                                              iv_tree_folder_title =  COND char30( WHEN l_v_delivery_exists EQ abap_true THEN l_v_title
                                                                                   ELSE space )
                                              iv_is_tree = COND xfeld( WHEN l_v_delivery_exists EQ abap_true  THEN abap_false
                                                                       ELSE abap_true )
                                              iv_tree_title = COND char30( WHEN l_v_delivery_exists EQ abap_true  THEN space
                                                                           ELSE l_v_title )
                                              iv_tree_level = COND int2( WHEN l_v_delivery_exists EQ abap_true  THEN 1
                                                                         ELSE 0 )
                                              iv_is_tabstrip = abap_true
                                              ).

            LOOP AT mt_deliveries ASSIGNING FIELD-SYMBOL(<l_s_invoice_delivery>) WHERE vbeln = <l_s_invoice_line>-vgbel AND posnr = <l_s_invoice_line>-vgpos.

              DATA(l_t_deliveries_alv) = VALUE lcl_tools=>tt_invoice_deliveries_alv( ( <l_s_invoice_delivery> ) ).

              l_v_title = |{ <l_s_invoice_delivery>-vbeln ALPHA = OUT }{ <l_s_invoice_delivery>-posnr ALPHA = OUT }|.

              "If deliveries exist - output them as subnodes of items
              mo_alv_transformer->add_alv_view( iv_structure_name = lcl_tools=>c_structure_delivery
                                                it_outtab = l_t_deliveries_alv
                                                iv_tree_title = l_v_title
                                                iv_is_tree = abap_true
                                                iv_tree_level = 1 ).

            ENDLOOP.

          ENDLOOP.
        ENDLOOP.

      CATCH cx_xx_shared_exception.                     "#EC NO_HANDLER
    ENDTRY.

  ENDMETHOD.

Once nodes are setup ALV just needs to be run using class CL_XX_ALV_DISPLAY

  METHOD display.

    TRY .
        "Adding of this class as ALV transformer module
        mo_alv_display->add_alv_transformer( mo_alv_transformer ).

        "Displays ALV
        mo_alv_display->display_alv( ).

      CATCH cx_xx_shared_exception.                     "#EC NO_HANDLER
    ENDTRY.

  ENDMETHOD.

Example of usage

For demonstration purposes I have created report Z_DMEE_INVOICE_EXTRACT that comprises all classes mentioned above.

I prepared 3 invoices with deliver and without delivery to present data in XML and in ALV:

As you see DMEE Tree Type is not available on selection screen but it is defined as NO-DISPLAY parameter with default value ‘ZZIV’. The great thing is that it is enough for Search Help for DMEE Tree to be restricted only to trees that are created under this type!

I let ‘Display Only’ marked as I first wanted to present the XML and do not need to download it.

Running report produces first an XML view and as expected there are 3 headers because of sorting in my DMEE Tree type that tells to generate one Header for each invoice.

Expanded it shows each line as desired:

Creator is as well filled in but just cut out in screenshot.

In DMEE Tree there was a condition that ‘Delivery’ XML node should not be output if the Delivery number is empty – so for sales-order-based billing this node is not expected and exactly this is output in XML:

Next screen after going through XML will present report output in hierarchy based on DME:

On the left side hierarchy of invoices is visible:

And on the right side the data of each line is visible for each hierarchy node which was selected:

That would be it in case of own DMEE creation – I hope you enjoyed it and see the potential in this solution to get rid of unnecessary mapping tables / mapping in ABAP which can simply be replaced by any mapping done in DMEE.

Full report code

For those who want to try out the whole solution and see how it works here is the full report code ready to copy and upload to SAP. Please note that some part of the code is written in ABAP 7.4 so if you get syntax errors then just change this parts of coding to normal data declaration.

REPORT z_dmee_invoice_extract.

"Custom DMEE Tree Type handling
INCLUDE z_dmee_invoice_extract_lcl00."Tools and data types
INCLUDE z_dmee_invoice_extract_ssc.  "Selection Screen
INCLUDE z_dmee_invoice_extract_lcl01."Screen processing class
INCLUDE z_dmee_invoice_extract_lcl02."ALV processing
INCLUDE z_dmee_invoice_extract_lcl03."Main report processing

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_vari.
  lcl_screen=>call_f4( lcl_tools=>c_f4_var ).

START-OF-SELECTION.
  lcl_screen=>get_instance( )->initialize( ).
  lcl_report=>get_instance( )->process( ).

Include Z_DMEE_INVOICE_EXTRACT_LCL00:

CLASS lcl_tools DEFINITION FINAL ABSTRACT.

  PUBLIC SECTION.

    "main processing types
    TYPES: tt_invoice_header TYPE SORTED TABLE OF zdmee_invoice_header WITH UNIQUE KEY primary_key COMPONENTS vbeln.
    TYPES: tt_invoice_lines TYPE SORTED TABLE OF zdmee_invoice_line WITH UNIQUE KEY primary_key COMPONENTS vbeln posnr.
    TYPES: tt_invoice_deliveries TYPE SORTED TABLE OF zdmee_delivery_line WITH UNIQUE KEY primary_key COMPONENTS vbeln posnr.

    "ALV display is using only standard tables so need to convert data before adding to ALV
    TYPES: tt_invoice_header_alv TYPE STANDARD TABLE OF zdmee_invoice_header WITH KEY primary_key COMPONENTS vbeln.
    TYPES: tt_invoice_lines_alv TYPE STANDARD TABLE OF zdmee_invoice_line WITH KEY primary_key COMPONENTS vbeln posnr.
    TYPES: tt_invoice_deliveries_alv TYPE STANDARD TABLE OF zdmee_delivery_line WITH KEY primary_key COMPONENTS vbeln posnr.

    CONSTANTS: c_structure_header TYPE tabname VALUE 'ZDMEE_INVOICE_HEADER'.
    CONSTANTS: c_structure_line TYPE tabname VALUE 'ZDMEE_INVOICE_LINE'.
    CONSTANTS: c_structure_delivery TYPE tabname VALUE 'ZDMEE_DELIVERY_LINE'.

    CONSTANTS c_f4_var TYPE fieldname VALUE 'VARIANT'.
    CONSTANTS: c_dmee_tree_type TYPE dmee_treetype VALUE 'ZZIV'.
    CONSTANTS: c_act_disp TYPE c LENGTH 2 VALUE '03' ##NO_TEXT.

    "for selection screen options
    CLASS-DATA: l_v_sccr_sdbil TYPE vbrk-vbeln.
    CLASS-DATA: l_v_sccr_sddate TYPE fkdat.
    CLASS-DATA: l_v_sccr_sdtype TYPE rv60a-fkart.
    CLASS-DATA: l_v_sccr_sdorg TYPE vkorg.

ENDCLASS.

Include Z_DMEE_INVOICE_EXTRACT_SSC:

SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE text-fb1.

PARAMETERS: p_comp TYPE bukrs OBLIGATORY DEFAULT '1000'.

SELECT-OPTIONS: so_sddoc FOR lcl_tools=>l_v_sccr_sdbil.
SELECT-OPTIONS: so_sddat FOR lcl_tools=>l_v_sccr_sddate.

SELECTION-SCREEN END OF BLOCK b01.

SELECTION-SCREEN BEGIN OF BLOCK b02 WITH FRAME TITLE text-fb2.

SELECT-OPTIONS: so_sdorg FOR lcl_tools=>l_v_sccr_sdorg.
SELECT-OPTIONS: so_sdtyp FOR lcl_tools=>l_v_sccr_sdtype.

SELECTION-SCREEN END OF BLOCK b02.

SELECTION-SCREEN BEGIN OF BLOCK b03 WITH FRAME TITLE text-fb3.

PARAMETERS: p_disp TYPE xtest DEFAULT 'X'.
PARAMETERS: tree_typ TYPE dmee_tree_head-tree_type DEFAULT lcl_tools=>c_dmee_tree_type NO-DISPLAY.
PARAMETERS: p_tree TYPE dmee_tree_head-tree_id OBLIGATORY.
PARAMETERS: p_vari TYPE slis_vari.

SELECTION-SCREEN END OF BLOCK b03.

Include Z_DMEE_INVOICE_EXTRACT_LCL01:

CLASS lcl_screen DEFINITION FINAL CREATE PRIVATE.

  PUBLIC SECTION.

    CLASS-METHODS get_instance RETURNING VALUE(r_o_screen) TYPE REF TO lcl_screen.
    CLASS-METHODS call_f4 IMPORTING i_v_field TYPE fieldname.
    CLASS-METHODS ask_for_variants.
    METHODS: initialize. "saves all selection-screen parameters for internal processing

    CLASS-DATA: mt_billing_docs TYPE RANGE OF vbeln_vf.
    CLASS-DATA: mt_billing_date TYPE RANGE OF fkdat.
    CLASS-DATA: mt_billing_types TYPE RANGE OF fkart.
    CLASS-DATA: mt_sales_org TYPE RANGE OF vkorg.
    CLASS-DATA: mv_dmee_tree TYPE dmee_treeid.
    CLASS-DATA: ms_dmee_additional_data TYPE zdmee_invoice_add_data.
    CLASS-DATA: mv_company_code TYPE bukrs.
    CLASS-DATA: mv_dmee_is_xml TYPE xfeld.
    CLASS-DATA: mv_display_only TYPE xfeld.
    CLASS-DATA: mv_layout TYPE char12.

  PRIVATE SECTION.
    CLASS-DATA m_o_screen TYPE REF TO lcl_screen.

    METHODS auth_company_code_single IMPORTING VALUE(i_v_company_code) TYPE bukrs
                                     RETURNING VALUE(r_v_authority_ok) TYPE boolean.
    METHODS get_additional_dme_params RETURNING VALUE(r_s_additional_data) TYPE zdmee_invoice_add_data.
    METHODS check_dmee_format RETURNING VALUE(r_v_is_xml) TYPE xfeld.

ENDCLASS.

CLASS lcl_screen IMPLEMENTATION.

  METHOD get_instance.

    IF m_o_screen IS INITIAL.
      CREATE OBJECT m_o_screen.
    ENDIF.
    r_o_screen = m_o_screen.

  ENDMETHOD.

  METHOD call_f4.

    CASE i_v_field.
      WHEN lcl_tools=>c_f4_var.

        ask_for_variants( ).

      WHEN OTHERS.
        "do nothing
    ENDCASE.

  ENDMETHOD.

  METHOD ask_for_variants.
    DATA: l_v_exit TYPE c LENGTH 1.
    DATA: l_s_layout TYPE disvariant.

    "in case of ALV used in this report it will always be structure name + report name
    l_s_layout-report = |{ lcl_tools=>c_structure_header }{ sy-repid }|.
    l_s_layout-username = sy-uname.
    CALL FUNCTION 'LT_F4_FOR_VARIANTS'
      EXPORTING
        is_variant         = l_s_layout
        i_display_via_grid = 'X'
      IMPORTING
        e_exit             = l_v_exit
        es_variant         = l_s_layout
      EXCEPTIONS
        not_found          = 1
        OTHERS             = 2.
    IF sy-subrc NE 0.
      MESSAGE text-002 TYPE 'S'. "no layout found
    ELSE.
      IF l_v_exit IS INITIAL.
        p_vari = l_s_layout-variant.
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD initialize.

    IF auth_company_code_single( p_comp ) NE abap_true.
      MESSAGE e060(f0) WITH p_comp.
    ENDIF.
    me->mv_company_code = p_comp.
    me->mt_billing_docs = so_sddoc[].
    me->mt_billing_date = so_sddat[].
    me->mt_billing_types = so_sdtyp[].
    me->mt_sales_org = so_sdorg[].
    me->mv_dmee_tree = p_tree.
    me->mv_display_only = p_disp.
    me->mv_layout = p_vari.
    me->ms_dmee_additional_data = get_additional_dme_params( ).
    me->mv_dmee_is_xml = check_dmee_format( ).

  ENDMETHOD.

  METHOD auth_company_code_single.

    AUTHORITY-CHECK OBJECT 'F_BKPF_BUK'
    ID 'BUKRS' FIELD i_v_company_code
    ID 'ACTVT' FIELD lcl_tools=>c_act_disp.
    IF sy-subrc EQ 0.
      r_v_authority_ok  = abap_true.
    ELSE.
      r_v_authority_ok = abap_false.
    ENDIF.

  ENDMETHOD.

  METHOD get_additional_dme_params.
    DATA: l_s_company_address TYPE szadr_addr1_complete.

    SELECT SINGLE bukrs butxt ort01 land1 waers stceg adrnr
      FROM t001
      INTO r_s_additional_data
      WHERE bukrs = mv_company_code.

    CALL FUNCTION 'ADDR_GET_COMPLETE'
      EXPORTING
        addrnumber              = r_s_additional_data-adrnr
      IMPORTING
        addr1_complete          = l_s_company_address
      EXCEPTIONS
        parameter_error         = 1
        address_not_exist       = 2
        internal_error          = 3
        wrong_access_to_archive = 4
        address_blocked         = 5
        OTHERS                  = 6.
    IF sy-subrc EQ 0.
      READ TABLE l_s_company_address-addr1_tab ASSIGNING FIELD-SYMBOL(<l_s_company_address>) INDEX 1.
      IF sy-subrc EQ 0.
        r_s_additional_data-post_code1 = <l_s_company_address>-data-post_code1.
        r_s_additional_data-street = <l_s_company_address>-data-street.
        r_s_additional_data-house_num1 = <l_s_company_address>-data-house_num1.
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD check_dmee_format.

    SELECT SINGLE xml_tree
      FROM  dmee_tree
      INTO r_v_is_xml
      WHERE tree_type = lcl_tools=>c_dmee_tree_type
      AND tree_id = mv_dmee_tree.

  ENDMETHOD.

ENDCLASS.

Include Z_DMEE_INVOICE_EXTRACT_LCL02:

CLASS lcl_alv_display DEFINITION FINAL CREATE PUBLIC.


  PUBLIC SECTION.
    METHODS: setup_alv.

    METHODS: constructor IMPORTING i_t_invoice_headers    TYPE lcl_tools=>tt_invoice_header
                                   i_t_invoice_lines      TYPE lcl_tools=>tt_invoice_lines
                                   i_t_invoice_deliveries TYPE lcl_tools=>tt_invoice_deliveries.

  PRIVATE SECTION.
    CLASS-DATA m_o_screen TYPE REF TO lcl_screen.
    DATA: mo_alv_transformer TYPE REF TO cl_xx_alv_transformer.
    DATA: mo_alv_display TYPE REF TO cl_xx_alv_display.
    DATA: mt_headers TYPE lcl_tools=>tt_invoice_header.
    DATA: mt_lines TYPE lcl_tools=>tt_invoice_lines.
    DATA: mt_deliveries TYPE lcl_tools=>tt_invoice_deliveries.

    "ALV display
    METHODS:
      setup_alv_nodes,
      display.

ENDCLASS.

CLASS lcl_alv_display IMPLEMENTATION.

  METHOD constructor.
    m_o_screen = lcl_screen=>get_instance( ).
    mo_alv_transformer = NEW cl_xx_alv_transformer( ).
    mo_alv_display = NEW cl_xx_alv_display( ).
    mt_headers = i_t_invoice_headers.
    mt_lines = i_t_invoice_lines.
    mt_deliveries = i_t_invoice_deliveries.

  ENDMETHOD.

  METHOD setup_alv.

    setup_alv_nodes( ).
    display( ).

  ENDMETHOD.

  METHOD setup_alv_nodes.
    DATA: l_v_header_title TYPE sytitle.
    DATA: l_v_title        TYPE char30.

    l_v_header_title = text-001.

    "inform user what is happening now
    CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
      EXPORTING
        text = text-i01.

    TRY.
        "add single invoice ALV header
        mo_alv_transformer->add_alv_view( iv_is_header = abap_true
                                          iv_header_title  = l_v_header_title ).

        "Folder for each invoice
        LOOP AT mt_headers ASSIGNING FIELD-SYMBOL(<l_s_invoice_header>).
          WRITE <l_s_invoice_header>-vbeln TO l_v_title.

          DATA(l_t_headers_alv) = VALUE lcl_tools=>tt_invoice_header_alv( ( <l_s_invoice_header> ) ).

          mo_alv_transformer->add_alv_view( iv_structure_name = lcl_tools=>c_structure_header
                                            iv_default_variant = m_o_screen->mv_layout
                                            it_outtab   = l_t_headers_alv
                                            iv_is_tree_folder = abap_true
                                            iv_tree_folder_title = l_v_title ).

          LOOP AT mt_lines ASSIGNING FIELD-SYMBOL(<l_s_invoice_line>) WHERE vbeln = <l_s_invoice_header>-vbeln.

            DATA(l_t_lines_alv) = VALUE lcl_tools=>tt_invoice_lines_alv( ( <l_s_invoice_line> ) ).

            l_v_title = |{ <l_s_invoice_line>-posnr ALPHA = OUT }|.

            "If delivery exists for a line - make a folder
            "If not - just output items as subnodes
            IF line_exists( mt_deliveries[ vbeln = <l_s_invoice_line>-vgbel ] ).
              DATA(l_v_delivery_exists) = abap_true.
            ELSE.
              CLEAR l_v_delivery_exists.
            ENDIF.

            mo_alv_transformer->add_alv_view( iv_structure_name = lcl_tools=>c_structure_line
                                              it_outtab = l_t_lines_alv
                                              iv_is_tree_folder = COND xfeld( WHEN l_v_delivery_exists EQ abap_true THEN abap_true
                                                                              ELSE abap_false )
                                              iv_tree_folder_title =  COND char30( WHEN l_v_delivery_exists EQ abap_true THEN l_v_title
                                                                                   ELSE space )
                                              iv_is_tree = COND xfeld( WHEN l_v_delivery_exists EQ abap_true  THEN abap_false
                                                                       ELSE abap_true )
                                              iv_tree_title = COND char30( WHEN l_v_delivery_exists EQ abap_true  THEN space
                                                                           ELSE l_v_title )
                                              iv_tree_level = COND int2( WHEN l_v_delivery_exists EQ abap_true  THEN 1
                                                                         ELSE 0 )
                                              iv_is_tabstrip = abap_true
                                              ).

            LOOP AT mt_deliveries ASSIGNING FIELD-SYMBOL(<l_s_invoice_delivery>) WHERE vbeln = <l_s_invoice_line>-vgbel AND posnr = <l_s_invoice_line>-vgpos.

              DATA(l_t_deliveries_alv) = VALUE lcl_tools=>tt_invoice_deliveries_alv( ( <l_s_invoice_delivery> ) ).

              l_v_title = |{ <l_s_invoice_delivery>-vbeln ALPHA = OUT }{ <l_s_invoice_delivery>-posnr ALPHA = OUT }|.

              "If deliveries exist - output them as subnodes of items
              mo_alv_transformer->add_alv_view( iv_structure_name = lcl_tools=>c_structure_delivery
                                                it_outtab = l_t_deliveries_alv
                                                iv_tree_title = l_v_title
                                                iv_is_tree = abap_true
                                                iv_tree_level = 1 ).

            ENDLOOP.

          ENDLOOP.
        ENDLOOP.

      CATCH cx_xx_shared_exception.                     "#EC NO_HANDLER
    ENDTRY.

  ENDMETHOD.

  METHOD display.

    TRY .
        "Adding of this class as ALV transformer module
        mo_alv_display->add_alv_transformer( mo_alv_transformer ).

        "Displays ALV
        mo_alv_display->display_alv( ).

      CATCH cx_xx_shared_exception.                     "#EC NO_HANDLER
    ENDTRY.

  ENDMETHOD.

ENDCLASS.

Include Z_DMEE_INVOICE_EXTRACT_LCL03:

CLASS lcl_report DEFINITION FINAL CREATE PRIVATE.

  PUBLIC SECTION.

    CLASS-METHODS get_instance RETURNING VALUE(r_o_report) TYPE REF TO lcl_report.
    "Main processing
    METHODS process.

  PRIVATE SECTION.
    CLASS-DATA: m_o_report TYPE REF TO lcl_report.

    DATA: mt_invoice_header TYPE lcl_tools=>tt_invoice_header.
    DATA: mt_invoice_lines TYPE lcl_tools=>tt_invoice_lines.
    DATA: mt_invoice_deliveries TYPE lcl_tools=>tt_invoice_deliveries.

    DATA: mv_dmee_first_started TYPE xfeld.

    CLASS-DATA m_o_screen TYPE REF TO lcl_screen.
    DATA: mt_dmee_output TYPE tab_dmee_output_file.

    METHODS: constructor.
    METHODS: select_data.

    "selection methods
    METHODS:
      select_invoices,
      select_invoice_lines,
      select_deliveries,
      get_tax_code_for_item.

    "DME handler
    METHODS:
      generate_dmee,
      start_dmee_engine IMPORTING i_s_invoice TYPE zdmee_invoice_header,
      add_dmee_item IMPORTING i_v_invoice_header   TYPE zdmee_invoice_header
                              i_v_invoice_line     TYPE zdmee_invoice_line
                              i_v_invoice_delivery TYPE zdmee_delivery_line OPTIONAL,
      close_engine,
      check_if_new_file_needed IMPORTING i_s_dmee_data              TYPE zdmee_invoice_data
                               RETURNING VALUE(r_v_new_file_needed) TYPE xfeld,
      download_file,
      download_xml_file,
      download_flat_file,
      xml_file_start_download IMPORTING i_v_display_only TYPE xfeld
                                        i_v_filename     TYPE string.
    "ALV display
    METHODS: display_alv.

ENDCLASS.

CLASS lcl_report IMPLEMENTATION.

  METHOD get_instance.

    IF m_o_report IS NOT BOUND.
      CREATE OBJECT m_o_report.
    ENDIF.
    r_o_report = m_o_report.

  ENDMETHOD.

  METHOD constructor.

    me->m_o_screen = lcl_screen=>get_instance( ).

  ENDMETHOD.

  METHOD process.

    select_data( ).   "Data selection
    generate_dmee( ). "Handling of all DMEE functions
    display_alv( ).   "Call LCL_ALV_DISPLAY class

  ENDMETHOD.

  METHOD select_data.

    select_invoices( ).     "Select invoice headers- VBRK
    select_invoice_lines( )."Select invoice lines - VBRP
    select_deliveries( ).   "Select delivery data for invoices - LIKP and LIPS

  ENDMETHOD.

  METHOD select_invoices.

    SELECT vkorg vtweg spart bukrs vbeln fkart fktyp waerk
           fkdat inco1 kurrf zterm ernam erzet kunrg kunag
           stceg fkart_rl bstnk_vf landtx land1 xblnr fksto
           kidno zuonr vbtyp knumv
    FROM vbrk
    INTO TABLE mt_invoice_header
    WHERE vbeln IN m_o_screen->mt_billing_docs
      AND bukrs EQ m_o_screen->mv_company_code
      AND fkdat IN m_o_screen->mt_billing_date
      AND fkart IN m_o_screen->mt_billing_types
      AND vkorg IN m_o_screen->mt_sales_org.
    IF sy-subrc NE 0.

      MESSAGE text-003 TYPE 'I'.
      LEAVE TO SCREEN 0.

    ENDIF.

  ENDMETHOD.

  METHOD select_invoice_lines.

    IF mt_invoice_header IS NOT INITIAL.
      SELECT vbeln, posnr, vrkme, meins, fkimg, ntgew, brgew,
             gewei, volum, voleh, prsdt, netwr, vgbel, wavwr,
             mwsbp, cmpre, vgpos, vgtyp, aubel, aupos, matnr,
             arktx, matkl, werks, pstyv, aland, sktof, skfbp,
             ernam, ean11, fbuda, paobjnr
      FROM vbrp
      INTO TABLE @DATA(l_t_invoice_lines)
            FOR ALL ENTRIES IN @mt_invoice_header
            WHERE vbeln = @mt_invoice_header-vbeln.
      IF sy-subrc EQ 0.
        MOVE-CORRESPONDING l_t_invoice_lines TO mt_invoice_lines.
      ENDIF.

    ENDIF.

    get_tax_code_for_item( ). "Determine MWZKS from condition lines - KONV

  ENDMETHOD.

  METHOD select_deliveries.
    TYPES: tr_delivery TYPE RANGE OF vbeln_vl.

    "get all deliveries as base for selection
    DATA(l_t_delivery_reference) = VALUE tr_delivery( FOR ls_delivery IN mt_invoice_lines
          WHERE ( vgtyp = 'J' )
            LET sign = 'I'
                option = 'EQ'
             IN sign = sign
                option = option
          ( low = ls_delivery-vgbel )
          ).

    IF l_t_delivery_reference IS NOT INITIAL.
      SORT l_t_delivery_reference.
      DELETE ADJACENT DUPLICATES FROM l_t_delivery_reference COMPARING low.

      SELECT a~vbeln, b~posnr, a~ernam, a~erdat, a~lfart, a~wadat, a~lddat,
             a~lfdat, a~kodat, a~ablad, a~kunnr, a~btgew, a~wadat_ist, a~wauhr,
             b~matnr, b~matwa, b~matkl, b~werks, b~lgort, b~charg, b~lichn,
             b~kdmat, b~prodh, b~lfimg, b~meins, b~vrkme, b~ntgew, b~brgew,
             b~gewei, b~lgmng, b~ladgr, b~tragr, b~lgnum, b~bwart, b~mtart,
             b~ean11, b~pstyv
      FROM likp AS a
      JOIN lips AS b ON
      a~vbeln = b~vbeln
      INTO TABLE @DATA(l_t_deliveries)
            WHERE a~vbeln IN @l_t_delivery_reference.
      IF sy-subrc EQ 0.
        MOVE-CORRESPONDING l_t_deliveries TO mt_invoice_deliveries.
      ENDIF.

    ENDIF.

  ENDMETHOD.

  METHOD get_tax_code_for_item.

    IF mt_invoice_header IS NOT INITIAL.

      "get pricing conditions to determine tax code
      SELECT knumv, kposn, mwsk1
      FROM konv
       INTO TABLE @DATA(l_t_pricing_conditions)
       FOR ALL ENTRIES IN @mt_invoice_header
                    WHERE knumv = @mt_invoice_header-knumv.
      IF sy-subrc EQ 0.
        SORT l_t_pricing_conditions BY knumv DESCENDING kposn DESCENDING.
        DELETE l_t_pricing_conditions WHERE mwsk1 IS INITIAL.
        SORT l_t_pricing_conditions BY knumv DESCENDING kposn DESCENDING.
        DELETE ADJACENT DUPLICATES FROM l_t_pricing_conditions COMPARING knumv kposn mwsk1.

        LOOP AT mt_invoice_header ASSIGNING FIELD-SYMBOL(<l_s_single_invoice>).

          LOOP AT mt_invoice_lines ASSIGNING FIELD-SYMBOL(<l_s_invoice_item>) WHERE vbeln = <l_s_single_invoice>-vbeln.

            TRY .
                <l_s_invoice_item>-mwskz = l_t_pricing_conditions[ knumv = <l_s_single_invoice>-knumv
                kposn = <l_s_invoice_item>-posnr ]-mwsk1.

              CATCH cx_sy_itab_line_not_found.
                CONTINUE.
            ENDTRY.

          ENDLOOP.

        ENDLOOP.
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD generate_dmee.

    "for each invoice process DMEE
    LOOP AT mt_invoice_header ASSIGNING FIELD-SYMBOL(<l_s_invoice_header>).

      "start DME engine for invoice or keep it running if DME is already running
      start_dmee_engine( <l_s_invoice_header> ).

      "loop over each invoice item for currently processed invoice
      LOOP AT mt_invoice_lines ASSIGNING FIELD-SYMBOL(<l_s_invoice_line>) WHERE vbeln = <l_s_invoice_header>-vbeln.

        "loop over each delivery connected to invoice line or simply output invoice line if no delivery exists
        IF line_exists( mt_invoice_deliveries[ vbeln = <l_s_invoice_line>-vgbel posnr = <l_s_invoice_line>-vgpos ] ).
          LOOP AT mt_invoice_deliveries ASSIGNING FIELD-SYMBOL(<l_s_invoice_delivery>) WHERE vbeln = <l_s_invoice_line>-vgbel.
            "delivery-based-billing
            add_dmee_item( EXPORTING i_v_invoice_header = <l_s_invoice_header>
                                     i_v_invoice_line = <l_s_invoice_line>
                                     i_v_invoice_delivery = <l_s_invoice_delivery> ).
          ENDLOOP.
        ELSE.
          "order-based-billing
          add_dmee_item( EXPORTING i_v_invoice_header = <l_s_invoice_header>
                                   i_v_invoice_line = <l_s_invoice_line> ).
        ENDIF.

      ENDLOOP.
    ENDLOOP.

    "at the very end close DME engine
    close_engine( ).
    "and process display or download
    download_file( ).

  ENDMETHOD.

  METHOD start_dmee_engine.
    DATA: l_s_dmee_item        TYPE zdmee_invoice_data,
          l_t_dmee_sort_fields TYPE STANDARD TABLE OF dmee_tree_sort.

    l_s_dmee_item-zdmee_invoice_header = i_s_invoice.

    IF mv_dmee_first_started EQ abap_true.
      DATA(l_v_new_file_needed) = check_if_new_file_needed( l_s_dmee_item ).
    ENDIF.

    IF mv_dmee_first_started IS INITIAL
    OR l_v_new_file_needed IS NOT INITIAL.

      CALL FUNCTION 'DMEE_START'
        EXPORTING
          i_tree_type = lcl_tools=>c_dmee_tree_type
          i_tree_id   = m_o_screen->mv_dmee_tree
          item        = l_s_dmee_item
          param       = m_o_screen->ms_dmee_additional_data
*         uparam      = i_format_params
        TABLES
          file_output = mt_dmee_output
          sort_fields = l_t_dmee_sort_fields.

      IF mv_dmee_first_started IS INITIAL.
        mv_dmee_first_started = abap_true.
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD add_dmee_item.
    DATA: l_s_dmee_item TYPE zdmee_invoice_data.

    l_s_dmee_item-zdmee_invoice_header = i_v_invoice_header.
    l_s_dmee_item-zdmee_invoice_line = i_v_invoice_line.
    l_s_dmee_item-zdmee_delivery_line = i_v_invoice_delivery.

    CALL FUNCTION 'DMEE_PUT_ITEM'
      EXPORTING
        item        = l_s_dmee_item
        param       = m_o_screen->ms_dmee_additional_data
*       uparam      = params
      TABLES
*       item_tab    = tab_item
        file_output = mt_dmee_output.

  ENDMETHOD.

  METHOD close_engine.
    DATA: l_t_error_output   TYPE STANDARD TABLE OF fimsg.
    DATA: l_t_fpm_fields  TYPE STANDARD TABLE OF dmee_fpm_fields.

    CALL FUNCTION 'DMEE_END'
      TABLES
        file_output  = mt_dmee_output
        error_output = l_t_error_output
        fpm_fields   = l_t_fpm_fields.

  ENDMETHOD.

  METHOD check_if_new_file_needed.

    CALL FUNCTION 'DMEE_NEW_FILE'
      EXPORTING
        item      = i_s_dmee_data
      CHANGING
        xnew_file = r_v_new_file_needed.

    IF r_v_new_file_needed IS NOT INITIAL.

      close_engine( ).
      download_file( ).

    ENDIF.

  ENDMETHOD.

  METHOD download_file.

    CASE m_o_screen->mv_dmee_is_xml.
      WHEN abap_true.
        download_xml_file( ).
      WHEN abap_false.
        download_flat_file( ).
      WHEN OTHERS.
    ENDCASE.

  ENDMETHOD.

  METHOD download_xml_file.
    CONSTANTS: l_c_xml TYPE string VALUE 'XML'.
    DATA:
      l_v_filename TYPE string,
      l_v_fullpath TYPE string,
      l_v_result   TYPE i,
      l_v_path     TYPE string.

    IF m_o_screen->mv_display_only EQ abap_true.

      xml_file_start_download( EXPORTING i_v_display_only = abap_true
                                         i_v_filename = l_v_filename ).
    ELSE.
      CALL METHOD cl_gui_frontend_services=>file_save_dialog
        EXPORTING
          default_extension = l_c_xml
        CHANGING
          filename          = l_v_filename
          path              = l_v_path
          fullpath          = l_v_fullpath
          user_action       = l_v_result.

      IF l_v_result IS INITIAL.

        xml_file_start_download( EXPORTING i_v_display_only = abap_false
                                           i_v_filename = l_v_filename ).

      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD download_flat_file.
    CONSTANTS c_file_txt TYPE string VALUE 'TXT'.

    DATA:
      l_v_filename TYPE string,
      l_v_path     TYPE string,
      l_v_fullpath TYPE string,
      l_v_result   TYPE i.
    DATA: l_v_codepage TYPE cpcodepage.
    DATA: l_t_file_data TYPE STANDARD TABLE OF string WITH EMPTY KEY.

    IF m_o_screen->mv_display_only IS NOT INITIAL.

      "display only
      CALL FUNCTION 'POPUP_WITH_TABLE_DISPLAY'
        EXPORTING
          endpos_col   = 30
          endpos_row   = lines( mt_dmee_output )
          startpos_col = 10
          startpos_row = 1
          titletext    = text-004
*        IMPORTING
*         choise       = l_v_choice
        TABLES
          valuetab     = mt_dmee_output
        EXCEPTIONS
          break_off    = 1
          OTHERS       = 2.

    ELSE.
      "download
      LOOP AT mt_dmee_output ASSIGNING FIELD-SYMBOL(<l_s_dme_line>).

        APPEND <l_s_dme_line>-line TO l_t_file_data.

      ENDLOOP.

      "get default encoding by language
      CALL FUNCTION 'NLS_GET_FRONTEND_CP'
        EXPORTING
          langu                 = CONV spras( sy-langu ) ##OPERATOR
*         FETYPE                = 'MS'
        IMPORTING
          frontend_codepage     = l_v_codepage
        EXCEPTIONS
          illegal_syst_codepage = 1
          no_frontend_cp_found  = 2
          internal_or_db_error  = 3
          OTHERS                = 4.
      IF sy-subrc NE 0.
        l_v_codepage = '4110'.
      ENDIF.
      CONDENSE l_v_codepage NO-GAPS.

      CALL METHOD cl_gui_frontend_services=>file_save_dialog
        EXPORTING
          default_extension = CONV string( c_file_txt )
        CHANGING
          filename          = l_v_filename
          path              = l_v_path
          fullpath          = l_v_fullpath
          user_action       = l_v_result.

      IF l_v_result IS INITIAL.

        CALL FUNCTION 'GUI_DOWNLOAD'
          EXPORTING
            filename = l_v_filename
            codepage = CONV abap_encoding( l_v_codepage )
          TABLES
            data_tab = l_t_file_data
          EXCEPTIONS
            OTHERS   = 1.
        IF sy-subrc NE 0.
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
          WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ELSE.
          MESSAGE i427(fr) WITH l_v_filename.
        ENDIF.
      ENDIF.

    ENDIF.
  ENDMETHOD.

  METHOD xml_file_start_download.

    CALL FUNCTION 'DMEE_HANDLE_XML_DOC_PC'
      EXPORTING
        i_filename = i_v_filename
        i_save     = COND xfeld( WHEN i_v_display_only EQ abap_true THEN space
                                 ELSE abap_true )
        i_display  = i_v_display_only.

  ENDMETHOD.

  METHOD display_alv.

    NEW lcl_alv_display( i_t_invoice_headers = mt_invoice_header
                         i_t_invoice_lines = mt_invoice_lines
                         i_t_invoice_deliveries = mt_invoice_deliveries )->setup_alv( ).

  ENDMETHOD.

ENDCLASS.

Texts used in report:

That would be it in terms of own DMEE Tree Type – I hope that not only you have read through the blog but also will consider using this approach in your designs.

Thank you for reading,

Marek Turczyński

Be the first to leave a comment
You must be Logged on to comment or reply to a post.