1 2 3 47 Previous Next

ABAP Development

694 Posts

The idea behind this trivial solution is the possibility to export text data from an SAP system into PowerPoint format using a given template. In my experience, multinational companies usually have a default PowerPoint template for presentations, which includes: the big company logo, strict rules for what should the header and the footer include, and so on. These are particularly important for status update presentations in different areas.

Although I had this requirement in the PPM sector, that is, Portfolio and Project Management, I chose to post this in ABAP development as it might be used in other areas as well.

template.png

As a prerequisite, this solution only works beginning with Office 2007 as it requires Office Open XML. Therefore, the iXML Library must be available on the SAP Application Server.

 

The following steps need to be performed in order to achieve a first test:

1. Create a demo template.

It is mandatory to set a custom property for each of the slides in the presentation, with:

name = "&&slide*&&"

type = "text"

value = "slide*"

(where * represents the slide number)

Advanced Properties.png

2. Create an ABAP/4 class with the following source-code or use the one attached bellow:

 

class ZCL_PPTX definition
  public
  create public .

public section.
*"* public components of class ZCL_PPTX
*"* do not include other source files here!!!

  constants MC_SCHEMA type STRING value 'http://schemas.openxmlformats.org/drawingml/2006/main'. "#EC NOTEXT

  methods CONSTRUCTOR
    importing
      !IM_V_CONTENT type XSTRING
    raising
      ZCX_PPTX .
  methods GENERATE
    importing
      !IM_T_TEXTS type ZXFILE_T_NAME_VALUE_PAIR
    returning
      value(RE_V_FILE) type XSTRING .
protected section.
*"* protected components of class ZCL_PPTX
*"* do not include other source files here!!!

  data MO_ZIP type ref to CL_ABAP_ZIP .
  data MO_IXML type ref to IF_IXML .
  data MO_IXML_DOCPROPS_CUSTOM type ref to IF_IXML_DOCUMENT .

  methods GET_FILE
    importing
      !IM_V_FILEPATH type STRING
    returning
      value(RE_O_DOCUMENT) type ref to IF_IXML_DOCUMENT .
  methods UPDATE_FILE
    importing
      !IM_V_FILEPATH type STRING
      !IM_O_DOCUMENT type ref to IF_IXML_DOCUMENT .
  methods ADD_FILE
    importing
      !IM_V_FILEPATH type STRING
      !IM_O_DOCUMENT type ref to IF_IXML_DOCUMENT .
  methods UPDATE_TEXTS
    importing
      !IM_T_TEXTS type ZXFILE_T_NAME_VALUE_PAIR
      !IM_O_IXML_NODE type ref to IF_IXML_NODE .
  methods GET_INDICATORS
    returning
      value(RE_T_INDICATORS) type ZXFILE_T_NAME_VALUE_PAIR .
private section.
*"* private components of class ZCL_PPTX
*"* do not include other source files here!!!
ENDCLASS.



CLASS ZCL_PPTX IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZCL_PPTX->ADD_FILE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_V_FILEPATH                  TYPE        STRING
* | [--->] IM_O_DOCUMENT                  TYPE REF TO IF_IXML_DOCUMENT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD ADD_FILE.

  DATA :
    lo_ostream  TYPE REF TO if_ixml_ostream,
    lv_content  TYPE xstring.

* create output stream
  lo_ostream = mo_ixml->create_stream_factory( )->create_ostream_xstring( string = lv_content ).

* set encoding to UTF-8 (Unicode Transformation Format)
* 8-bit variable-width encoding maximizes compatibility with ASCII
  lo_ostream->set_encoding( encoding = mo_ixml->create_encoding( character_set = 'UTF-8' byte_order = 0 ) ).

* Set Pretty Print
  lo_ostream->set_pretty_print( abap_true ).

* render document
  mo_ixml->create_renderer( ostream = lo_ostream document = im_o_document )->render( ).

* add file
  mo_zip->add( name = im_v_filepath content = lv_content ).

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_PPTX->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_V_CONTENT                  TYPE        XSTRING
* | [!CX!] ZCX_PPTX
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD constructor.

  IF im_v_content IS INITIAL.
    TRY.
      RAISE EXCEPTION TYPE zcx_pptx.
    ENDTRY.
  ENDIF.

* get iXML library instance
  mo_ixml = cl_ixml=>create( ).

* load OpenXML document
  CREATE OBJECT mo_zip.
  mo_zip->load(
    EXPORTING
      zip            = im_v_content
    EXCEPTIONS
      zip_parse_error = 1
      OTHERS          = 2 ).

  IF NOT sy-subrc IS INITIAL.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

  mo_ixml_docprops_custom = get_file( im_v_filepath = 'docProps/custom.xml' ).

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_PPTX->GENERATE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_T_TEXTS                    TYPE        ZXFILE_T_NAME_VALUE_PAIR
* | [<-()] RE_V_FILE                      TYPE        XSTRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD generate.

  CLEAR : re_v_file.

  DATA :
    lo_ixml_document      TYPE REF TO if_ixml_document,
    lo_ixml_document_rels TYPE REF TO if_ixml_document,
    lt_indicators        TYPE zxfile_t_name_value_pair,
    lt_files              TYPE cl_abap_zip=>t_files,
    lv_dummy              TYPE string,                      "#EC NEEDED
    lv_slide              TYPE string,
    lv_value              TYPE string,
    lv_filename          TYPE string,
    lv_filename_rels      TYPE string.

  lt_indicators[] = get_indicators( ).
  lt_files[]      = mo_zip->files[].

  FIELD-SYMBOLS : <fs_file> TYPE cl_abap_zip=>t_file.
  LOOP AT lt_files[] ASSIGNING <fs_file>
                    WHERE name CP 'ppt/slides/slide*.xml'.
    CLEAR:
      lv_dummy,
      lv_slide,
      lv_value,
      lv_filename,
      lv_filename_rels.

    SPLIT <fs_file>-name AT 'ppt/slides/' INTO lv_dummy lv_slide.
    SPLIT lv_slide      AT '.'          INTO lv_value lv_dummy.

    FIELD-SYMBOLS : <fs_indicator> TYPE zxfile_s_name_value_pair.
    READ TABLE lt_indicators[] ASSIGNING <fs_indicator>
                              WITH KEY value = lv_value.
    IF NOT sy-subrc IS INITIAL.
*    file not relevant, process next
      CONTINUE.
    ENDIF.

*  get .xml file
    lo_ixml_document = get_file( im_v_filepath = <fs_file>-name ).

*  get .rels file
    CONCATENATE 'ppt/slides/_rels/' lv_value '.xml.rels' INTO lv_filename_rels.
    lo_ixml_document_rels = get_file( im_v_filepath = lv_filename_rels ).

*  check indicators
    IF NOT <fs_indicator>-name IS INITIAL.
      update_texts( EXPORTING im_t_texts    = im_t_texts[]
                              im_o_ixml_node = lo_ixml_document ).

    ENDIF.

*  trigger update
    lv_filename = <fs_file>-name.
    update_file( im_v_filepath = lv_filename      im_o_document = lo_ixml_document ).
    update_file( im_v_filepath = lv_filename_rels im_o_document = lo_ixml_document_rels ).
  ENDLOOP.

  re_v_file = mo_zip->save( ).

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZCL_PPTX->GET_FILE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_V_FILEPATH                  TYPE        STRING
* | [<-()] RE_O_DOCUMENT                  TYPE REF TO IF_IXML_DOCUMENT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_file.

  DATA :
    lo_stream_factory TYPE REF TO if_ixml_stream_factory,
    lo_istream        TYPE REF TO if_ixml_istream,
    lv_content        TYPE xstring.

  mo_zip->get( EXPORTING  name                    = im_v_filepath
              IMPORTING  content                = lv_content
              EXCEPTIONS zip_index_error        = 1
                          zip_decompression_error = 2
                          OTHERS                  = 3 ).

  IF NOT sy-subrc IS INITIAL.
    RETURN.
  ENDIF.

* create the document
  re_o_document = mo_ixml->create_document( ).

* create the stream factory
  lo_stream_factory = mo_ixml->create_stream_factory( ).

* create the input stream
  lo_istream = lo_stream_factory->create_istream_xstring( lv_content ).

* parse document
  IF NOT mo_ixml->create_parser( document      = re_o_document
                                istream        = lo_istream
                                stream_factory = lo_stream_factory )->parse( ) is INITIAL.
    CLEAR : re_o_document.
  ENDIF.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZCL_PPTX->GET_INDICATORS
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RE_T_INDICATORS                TYPE        ZXFILE_T_NAME_VALUE_PAIR
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD GET_INDICATORS.

  REFRESH : re_t_indicators[].

  DATA :
    lo_ixml_iterator  TYPE REF TO if_ixml_node_iterator,
    lo_ixml_element    TYPE REF TO if_ixml_element,
    ls_indicator      TYPE zxfile_s_name_value_pair.

* get the corresponding entries in the custom .xml to control the generation
  lo_ixml_iterator  = mo_ixml_docprops_custom->get_elements_by_tag_name( name = 'property' )->create_iterator( ).

* get the first element
  lo_ixml_element ?= lo_ixml_iterator->get_next( ).
  WHILE lo_ixml_element IS BOUND.
    CLEAR ls_indicator.
*  get name
    ls_indicator-name = condense( lo_ixml_element->get_attribute( name = 'name' ) ).

    IF ls_indicator-name CP '&&*&&'.
*    get value
      lo_ixml_element    = lo_ixml_element->find_from_name( name = 'lpwstr' namespace = 'vt' ).
      ls_indicator-value = condense( val = lo_ixml_element->get_value( ) ).

*    insert indicator
      INSERT ls_indicator INTO TABLE re_t_indicators[].
    ENDIF.

*  get next element
    lo_ixml_element ?= lo_ixml_iterator->get_next( ).
  ENDWHILE.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZCL_PPTX->UPDATE_FILE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_V_FILEPATH                  TYPE        STRING
* | [--->] IM_O_DOCUMENT                  TYPE REF TO IF_IXML_DOCUMENT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD UPDATE_FILE.

  mo_zip->delete( EXPORTING  name            = im_v_filepath
                  EXCEPTIONS zip_index_error = 1
                            OTHERS          = 2 ).

  IF NOT sy-subrc IS INITIAL.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

  IF im_o_document IS BOUND.
    add_file( im_v_filepath = im_v_filepath im_o_document = im_o_document ).
  ENDIF.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method ZCL_PPTX->UPDATE_TEXTS
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_T_TEXTS                    TYPE        ZXFILE_T_NAME_VALUE_PAIR
* | [--->] IM_O_IXML_NODE                TYPE REF TO IF_IXML_NODE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD UPDATE_TEXTS.

  DATA :
    lo_classdescr    TYPE REF TO cl_abap_classdescr,
    lo_exception    TYPE REF TO cx_sy_ref_is_initial,
    lo_ixml_texts    TYPE REF TO if_ixml_node_collection,
    lo_ixml_iterator TYPE REF TO if_ixml_node_iterator,
    lo_ixml_document TYPE REF TO if_ixml_document,
    lo_ixml_element  TYPE REF TO if_ixml_element,
    lo_ixml_node    TYPE REF TO if_ixml_node,
    lv_message      TYPE string,
    lv_text          TYPE string,
    lv_result        TYPE string.

  TRY.
*    determine all texts of the corresponding node
      lo_classdescr ?= cl_abap_classdescr=>describe_by_object_ref( im_o_ixml_node ).
      READ TABLE lo_classdescr->interfaces WITH KEY name = 'IF_IXML_DOCUMENT'
                                          TRANSPORTING NO FIELDS.
      IF sy-subrc IS INITIAL.
*      document
        lo_ixml_document ?= im_o_ixml_node.
        lo_ixml_texts    = lo_ixml_document->get_elements_by_tag_name_ns(
                              name = 't'
                              uri  = mc_schema ).

      ELSE.
        READ TABLE lo_classdescr->interfaces WITH KEY name = 'IF_IXML_ELEMENT'
                                            TRANSPORTING NO FIELDS.
        IF sy-subrc IS INITIAL.
*        element
          lo_ixml_element ?= im_o_ixml_node.
          lo_ixml_texts    = lo_ixml_element->get_elements_by_tag_name_ns(
                              name = 't'
                              uri  = mc_schema ).
        ELSE.
*        current object not supported
          RETURN.
        ENDIF.
      ENDIF.

*    get iterator
      lo_ixml_iterator = lo_ixml_texts->create_iterator( ).

*    get first node
      lo_ixml_node = lo_ixml_iterator->get_next( ).

      WHILE lo_ixml_node IS BOUND.
*      update slide
        lv_text = lo_ixml_node->get_value( ).

*      replace the corresponding text
        FIELD-SYMBOLS : <fs_text> TYPE zxfile_s_name_value_pair.
        LOOP AT im_t_texts[] ASSIGNING <fs_text>.
*        update component
          CONCATENATE '&&' <fs_text>-name '&&' INTO lv_result.
          REPLACE lv_result IN lv_text WITH <fs_text>-value.
        ENDLOOP.

*      set updated text
        lo_ixml_node->set_value( lv_text ).

*      get next node
        lo_ixml_node = lo_ixml_iterator->get_next( ).
      ENDWHILE.
    CATCH cx_sy_ref_is_initial INTO lo_exception.
      IF lo_exception->is_resumable EQ abap_false.
        lv_message = lo_exception->get_text( ).
        MESSAGE lv_message TYPE 'X'.
      ENDIF.
  ENDTRY.

ENDMETHOD.
ENDCLASS.

 

3. Create a demo report with the following source-code or use the one attached bellow:

 

REPORT zdemo_pptx.

DATA :
  gv_data TYPE xstring.

PARAMETERS :
  p_file TYPE localfile.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
  PERFORM open.

START-OF-SELECTION.
  PERFORM upload.
  PERFORM generate.
  PERFORM download.
END-OF-SELECTION.

FORM upload.
  DATA :
    lt_file      TYPE solix_tab,
    lv_filename  TYPE string,
    lv_filelength TYPE i.

  lv_filename = p_file.

  CALL METHOD cl_gui_frontend_services=>gui_upload
    EXPORTING
      filename                = lv_filename
      filetype                = 'BIN'
    IMPORTING
      filelength              = lv_filelength
    CHANGING
      data_tab                = lt_file
    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 NOT sy-subrc IS INITIAL.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

  CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
    EXPORTING
      input_length = lv_filelength
    IMPORTING
      buffer      = gv_data
    TABLES
      binary_tab  = lt_file
    EXCEPTIONS
      failed      = 1
      OTHERS      = 2.

  IF NOT sy-subrc IS INITIAL.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ENDFORM.

FORM generate.
  DATA :
    lo_pptx  TYPE REF TO zcl_pptx,
    lt_texts TYPE zxfile_t_name_value_pair,
    ls_text  TYPE zxfile_s_name_value_pair.

  TRY.
      CREATE OBJECT lo_pptx
        EXPORTING
          im_v_content = gv_data.
    CATCH zcx_pptx.
      MESSAGE text-001 TYPE 'E'.
  ENDTRY.

  ls_text-name  = 'TITLE'.
  ls_text-value = 'Automatically generated
PowerPoint'.
  APPEND ls_text TO lt_texts. CLEAR ls_text.

  ls_text-name  = '
SUBTITLE'.
  ls_text-value = '
using very little coding'.
  APPEND ls_text TO lt_texts. CLEAR ls_text.

  ls_text-name  = 'NAME'.
  ls_text-value = '
Your name (Ex. John Dr. Smith)'.
  APPEND ls_text TO lt_texts. CLEAR ls_text.

  ls_text-name  = 'DEPARTMENT'.
  ls_text-value = '
Your department name or abbreviation'.
  APPEND ls_text TO lt_texts. CLEAR ls_text.

  ls_text-name  = 'DATE'.
  ls_text-value =
sy-datum.
  APPEND ls_text TO lt_texts. CLEAR ls_text.


  CLEAR gv_data.
  gv_data = lo_pptx->generate( lt_texts ).
ENDFORM.

FORM download.
  DATA :
    lt_file      TYPE solix_tab,
    lv_filelength TYPE i,
    lv_filename  TYPE string,
    lv_fullpath  TYPE string,
    lv_path      TYPE string.

  CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
    EXPORTING
      buffer        = gv_data
    IMPORTING
      output_length = lv_filelength
    TABLES
      binary_tab    = lt_file.

  CALL METHOD cl_gui_frontend_services=>file_save_dialog
    CHANGING
      filename                  = lv_filename
      path                      = lv_path
      fullpath                  = lv_fullpath
    EXCEPTIONS
      cntl_error                = 1
      error_no_gui              = 2
      not_supported_by_gui      = 3
      invalid_default_file_name = 4
      OTHERS                    = 5.

  IF NOT sy-subrc IS INITIAL.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

  TRANSLATE lv_fullpath TO UPPER CASE.
  IF lv_fullpath NS '.PPTX'.
    CONCATENATE lv_fullpath '.PPTX' INTO lv_fullpath.
  ENDIF.

  CALL METHOD cl_gui_frontend_services=>gui_download
    EXPORTING
      bin_filesize            = lv_filelength
      filename                = lv_fullpath
      filetype                = 'BIN'
    CHANGING
      data_tab                = lt_file
    EXCEPTIONS
      file_write_error        = 1
      no_batch                = 2
      gui_refuse_filetransfer = 3
      invalid_type            = 4
      no_authority            = 5
      unknown_error          = 6
      header_not_allowed      = 7
      separator_not_allowed  = 8
      filesize_not_allowed    = 9
      header_too_long        = 10
      dp_error_create        = 11
      dp_error_send          = 12
      dp_error_write          = 13
      unknown_dp_error        = 14
      access_denied          = 15
      dp_out_of_memory        = 16
      disk_full              = 17
      dp_timeout              = 18
      file_not_found          = 19
      dataprovider_exception  = 20
      control_flush_error    = 21
      not_supported_by_gui    = 22
      error_no_gui            = 23
      OTHERS                  = 24.

  IF NOT sy-subrc IS INITIAL.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ENDFORM.

FORM open.
  CALL FUNCTION 'F4_FILENAME'
    EXPORTING
      program_name  = syst-cprog
      dynpro_number = syst-dynnr
      field_name    = space
    IMPORTING
      file_name    = p_file.
ENDFORM.

In the future, based on new requirements, I plan to extend this class to also include tables, new slides, and images. But if you get ahead of me, please feel free to add your code on SCN and give me a hint!

 

Tudor

I'm not entirely sure if a similar solution exists; I've tried searching through blog posts in the ABAP Development space and couldn't find any match (although there are over 1000 results for "abap xml").

 

Basically, more out of boredom and willing to learn more about the capabilities of the iXML Library, I made an ABAP/4 class that can transform any data (simple data, structure, table type) at any depth into XML format and vice versa. Even though, bringing XML to ABAP/4 only matches elements with the same name, ignoring extra data.

 

class.png

 

For example:

Given the following nested-structured local transparent table:

 

DATA :
  BEGIN OF ls_data.
        INCLUDE STRUCTURE spfli.
DATA :
      scarr   TYPE scarr,
      sflight TYPE sflight_tab1,
    END OF ls_data,
  lt_data LIKE TABLE OF ls_data.

 

Filled with test data:

 

SELECT *
       UP TO 10 ROWS
       FROM spfli
       INTO CORRESPONDING FIELDS OF TABLE lt_data.

FIELD-SYMBOLS : <fs_data> LIKE LINE OF lt_data.
LOOP AT lt_data ASSIGNING <fs_data>.
  SELECT SINGLE *
                FROM scarr
                INTO CORRESPONDING FIELDS OF <fs_data>-scarr
                WHERE carrid = <fs_data>-carrid.

  SELECT *
         FROM sflight
         INTO CORRESPONDING FIELDS OF TABLE <fs_data>-sflight
         WHERE carrid = <fs_data>-carrid
           AND connid = <fs_data>-connid.
ENDLOOP.

 

The result is:

data.png

The class looks as follows (I will also attach it as text at the bottom together with the demo report):

 

class ZCL_XML_UTIL definition
  public
  create public .

public section.
*"* public components of class ZCL_XML_UTIL
*"* do not include other source files here!!!

  methods CONSTRUCTOR .
  methods ABAP_TO_XML
    importing
      !IM_DATA type ANY
    exporting
      !EX_CONTENT type XSTRING
    raising
      ZCX_TYPE_NOT_SUPPORTED .
  methods XML_TO_ABAP
    importing
      !IM_CONTENT type XSTRING
    exporting
      !EX_DATA type ANY
    raising
      ZCX_TYPE_NOT_SUPPORTED .
protected section.
*"* protected components of class ZCL_XML_UTIL
*"* do not include other source files here!!!

  data MO_IXML type ref to IF_IXML .
  data MO_DOCUMENT type ref to IF_IXML_DOCUMENT .
private section.
*"* private components of class ZCL_XML_UTIL
*"* do not include other source files here!!!

  methods PROCESS
    importing
      !IM_NODE type ref to IF_IXML_NODE
      !IM_NAME type STRING
      !IM_DATA type ANY
    raising
      ZCX_TYPE_NOT_SUPPORTED .
  methods CREATE_ELEMENT
    importing
      !IM_NAME type STRING
      !IM_VALUE type STRING optional
    returning
      value(RE_ELEMENT) type ref to IF_IXML_ELEMENT .
  methods TRAVERSE
    importing
      !IM_CURRENT_NODE type ref to IF_IXML_NODE
    changing
      !CH_DATA type ANY
    raising
      ZCX_TYPE_NOT_SUPPORTED .
  class-methods AS_STRING
    importing
      !IM_DATA type ANY
    returning
      value(RE_DATA_STRING) type STRING .
ENDCLASS.



CLASS ZCL_XML_UTIL IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_XML_UTIL->ABAP_TO_XML
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_DATA                        TYPE        ANY
* | [<---] EX_CONTENT                     TYPE        XSTRING
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD abap_to_xml.

  CLEAR : ex_content.

* create .xml document
  mo_document = mo_ixml->create_document( ).

* set encoding to UTF-8 (Unicode Transformation Format)
* 8-bit variable-width encoding maximizes compatibility with ASCII
  mo_document->set_encoding( mo_ixml->create_encoding(
      byte_order    = 0
      character_set = 'UTF-8' ) ).

  IF NOT im_data IS INITIAL.
*   use mo_document as root
    process( im_node = mo_document
             im_name = 'data'
             im_data = im_data ).
  ENDIF.

* render .xml document with output stream
  mo_document->render(
    ostream = mo_ixml->create_stream_factory( )->create_ostream_xstring(
      string = ex_content ) ).
  FREE : mo_document.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_XML_UTIL=>AS_STRING
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_DATA                        TYPE        ANY
* | [<-()] RE_DATA_STRING                 TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD as_string.

  MOVE im_data TO re_data_string.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_XML_UTIL->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CONSTRUCTOR.

* get iXML library instance
  mo_ixml = cl_ixml=>create( ).

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_XML_UTIL->CREATE_ELEMENT
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_NAME                        TYPE        STRING
* | [--->] IM_VALUE                       TYPE        STRING(optional)
* | [<-()] RE_ELEMENT                     TYPE REF TO IF_IXML_ELEMENT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD CREATE_ELEMENT.

  DATA :
    lv_name TYPE string VALUE IS INITIAL.

* element names look cooler in lower case
  lv_name = to_lower( im_name ).

* create element with given name
  re_element = mo_document->create_element( name = lv_name ).

* if element is leaf, set corresponding value
  IF im_value IS SUPPLIED.
    re_element->set_value( im_value ).
  ENDIF.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_XML_UTIL->PROCESS
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_NODE                        TYPE REF TO IF_IXML_NODE
* | [--->] IM_NAME                        TYPE        STRING
* | [--->] IM_DATA                        TYPE        ANY
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD process.

  DATA :
    lo_type    TYPE REF TO cl_abap_typedescr,
    lo_struct  TYPE REF TO cl_abap_structdescr,
    lo_table   TYPE REF TO cl_abap_tabledescr,
    lo_data    TYPE REF TO data,
    lo_element TYPE REF TO if_ixml_element,
    lv_name    TYPE string VALUE IS INITIAL.

  FIELD-SYMBOLS :
    <ft_data>      TYPE STANDARD TABLE,
    <fs_component> TYPE abap_compdescr,
    <fs_data>      TYPE any,
    <fv_value>     TYPE any.

* determine ABAP/4 data type, support only:
* - simple type
* - structure
* - table type
  lo_type = cl_abap_typedescr=>describe_by_data( im_data ).

  CASE lo_type->kind.
    WHEN cl_abap_typedescr=>kind_elem.
*     create element
      lo_element = create_element(
          im_name  = im_name
          im_value = as_string( im_data ) ).

    WHEN cl_abap_typedescr=>kind_struct.
*     create parent element
      lo_element = create_element( im_name = im_name ).

*     process each structure component independently
      lo_struct ?= cl_abap_structdescr=>describe_by_data( im_data ).
      LOOP AT lo_struct->components ASSIGNING <fs_component>.
        ASSIGN COMPONENT <fs_component>-name OF STRUCTURE im_data TO <fv_value>.
        CHECK sy-subrc IS INITIAL.

        lv_name = <fs_component>-name.

        process( im_node = lo_element
                 im_name = lv_name
                 im_data = <fv_value> ).
      ENDLOOP.

    WHEN cl_abap_typedescr=>kind_table.
      lo_table ?= cl_abap_tabledescr=>describe_by_data( im_data ).
      CREATE DATA lo_data TYPE HANDLE lo_table.
      ASSIGN lo_data->* TO <ft_data>.
      <ft_data> = im_data.

*     create parent element
      lo_element = create_element( im_name = im_name ).

*     process each table line independently
      LOOP AT <ft_data> ASSIGNING <fs_data>.
       
process( im_node = lo_element
                 im_name = im_name
                 im_data = <fs_data> ).
     
ENDLOOP.

    WHEN OTHERS.
      TRY.
        RAISE EXCEPTION TYPE zcx_type_not_supported.
      ENDTRY.
  ENDCASE.

  IF lo_element IS BOUND.
    im_node->append_child( lo_element ).
  ENDIF.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_XML_UTIL->TRAVERSE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_CURRENT_NODE                TYPE REF TO IF_IXML_NODE
* | [<-->] CH_DATA                        TYPE        ANY
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD traverse.

  DATA :
    lo_type      TYPE REF TO cl_abap_typedescr,
    lo_struct    TYPE REF TO cl_abap_structdescr,
    lo_table     TYPE REF TO cl_abap_tabledescr,
    lo_data      TYPE REF TO data,
    lo_iterator  TYPE REF TO if_ixml_node_iterator,
    lo_node_list TYPE REF TO if_ixml_node_list,
    lo_node      TYPE REF TO if_ixml_node,
    lv_node_name TYPE string VALUE IS INITIAL,
    lv_match     TYPE boolean.

  FIELD-SYMBOLS :
    <ft_data>      TYPE STANDARD TABLE,
    <fs_component> TYPE abap_compdescr,
    <fs_data>      TYPE any,
    <fv_value>     TYPE any.

* determine ABAP/4 data type, support only:
* - simple type
* - structure
* - table type
  lo_type = cl_abap_typedescr=>describe_by_data( ch_data ).

  CASE lo_type->kind.
    WHEN cl_abap_typedescr=>kind_elem.
      ch_data = im_current_node->get_value( ).

    WHEN cl_abap_typedescr=>kind_struct.
      lo_node_list = im_current_node->get_children( ).

*     process each structure component independently
      lo_struct ?= cl_abap_structdescr=>describe_by_data( ch_data ).
      LOOP AT lo_struct->components ASSIGNING <fs_component>.
        ASSIGN COMPONENT <fs_component>-name OF STRUCTURE ch_data TO <fv_value>.
        CHECK sy-subrc IS INITIAL.

*       create new iterator with each step
        lo_iterator = lo_node_list->create_iterator( ).
        lo_node = lo_iterator->get_next( ).

        WHILE lo_node  IS BOUND
           OR lv_match EQ abap_true.

*         xml element names can contain lower case characters
          lv_node_name = to_upper( lo_node->get_name( ) ).

          IF lv_node_name EQ <fs_component>-name.
            lv_match = abap_true.
            EXIT.
          ENDIF.

          lo_node = lo_iterator->get_next( ).
        ENDWHILE.

        CHECK lv_match EQ abap_true.
        traverse( EXPORTING im_current_node = lo_node
                  CHANGING  ch_data         = <fv_value> ).
        CLEAR : lv_match.
      ENDLOOP.

    WHEN cl_abap_typedescr=>kind_table.
      lo_table ?= cl_abap_tabledescr=>describe_by_data( ch_data ).

      CREATE DATA lo_data TYPE HANDLE lo_table.
      ASSIGN lo_data->* TO <ft_data>.

      lo_node_list = im_current_node->get_children( ).
      lo_iterator = lo_node_list->create_iterator( ).
      lo_node = lo_iterator->get_next( ).

      WHILE lo_node IS BOUND.
        APPEND INITIAL LINE TO <ft_data> ASSIGNING <fs_data>.

        traverse( EXPORTING im_current_node = lo_node
                  CHANGING  ch_data         = <fs_data> ).

        lo_node = lo_iterator->get_next( ).
      ENDWHILE.

      IF NOT <ft_data> IS INITIAL.
        ch_data = <ft_data>.
      ENDIF.

    WHEN OTHERS.
      TRY.
        RAISE EXCEPTION TYPE zcx_type_not_supported.
      ENDTRY.
  ENDCASE.

ENDMETHOD.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_XML_UTIL->XML_TO_ABAP
* +-------------------------------------------------------------------------------------------------+
* | [--->] IM_CONTENT                     TYPE        XSTRING
* | [<---] EX_DATA                        TYPE        ANY
* | [!CX!] ZCX_TYPE_NOT_SUPPORTED
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD xml_to_abap.

  CLEAR : ex_data.

  DATA :
    lo_istream        TYPE REF TO if_ixml_istream,
    lo_stream_factory TYPE REF TO if_ixml_stream_factory,
    lo_iterator       TYPE REF TO if_ixml_node_iterator.

  ASSERT NOT im_content IS INITIAL.

  IF NOT ex_data IS REQUESTED.
    RETURN.
  ENDIF.

* create .xml document
  mo_document = mo_ixml->create_document( ).

  lo_stream_factory = mo_ixml->create_stream_factory( ).
  lo_istream = lo_stream_factory->create_istream_xstring( im_content ).

* parse .xml document with given input stream
  IF mo_ixml->create_parser(
      document       = mo_document
      istream        = lo_istream
      stream_factory = lo_stream_factory )->parse( ) IS INITIAL.

*   create iterator
    lo_iterator = mo_document->create_iterator( 0 ).

*   skip #document element
    lo_iterator->get_next( ).

*   start with root
    traverse( EXPORTING im_current_node = lo_iterator->get_next( )
              CHANGING  ch_data         = ex_data ).
  ENDIF.
  FREE : mo_document.

ENDMETHOD.
ENDCLASS.

 

Feedback is most welcomed!

 

Tudor

There is a lot of discussion about why we should use object oriented programming instead of plain old procedural. I have to admit it is sometimes difficult to come up with good reasons, besides the "Try it and will find out", so I'll blog about the reasons as I come upon them starting with: import parameters.

 

Last week I wanted to reuse a custom method that creates an inventory in WM, and the definition was something like this (zwm_linv_t is at table type of LINV):

 

Methods Create_Inventory
            importing !I_Link as LINK
                            !IT_Linv as zwm_linv_t




Why is this less then optimum design?

 

Side Note: Before you say, "Wait, that already uses OO ABAP", remember that using classes doesn't make the code object oriented. Object orientation is about design principles, not whether you use function modules or classes.

 

With that out of the way, why could this be improved upon?

  1. From the interface I get very little information about exactly what parameters are required since LINK has a lot of fields,
  2. I can't use "Mandatory" keyword because it applies to the entire LINK structure;
  3. I have to look inside the code to know which fields to fill (which in this case were only 3: date, warehouse number and storage type).

 

A better design would be (ignoring LINV):

 

Methods Create_Inventory
            importing !LGNUM TYPE LGNUM
                            !LGTYP TYPE LGTYP
                            !DATE TYPE DATUM





 

Again you might say "There are no objects there, that's not object oriented!"*. Well you could, but you would be missing the point. The main difference between procedural and pure OO is that in pure OO there are no structures, everything is a class. The fact that there no structures prevents you from doing stuff like the first example (and I've seen many people do that).

 

The main difference between structures and classes is that classes are responsible for their own consistency, while structures aren't very smart. In a class, the public and therefore "mandatory" attributes are filled in the constructor which makes sure that, IF an object (instance of a class) exists, those attributes have a value. With structures that doesn't happen, there's nothing to ensure it.

 

Let's make that clearer by replacing the method interface with one that uses an object with the attributes of LINK

 

Methods Create_Inventory
            importing !I_INVENTORY TYPE REF TO ZWM_INVENTORY.






When I want to create an instance of the import parameter I need to use the constructor, which will give a clear indication of the mandatory parameters (lack of polymorphism makes this trickier but...moving along). Unlike the first example I don't need to guess, I don't need to look into the code, I just call a method with explicit parameters, and in return I get a "bullet proof" import param.

 

If you ever used a BAPI and had to look in the code or debug to know exactly which fields to fill, you've come across the reason why structures allow very bad design, and shouldn't be used in APIs. Saves some one time effort on the side of the programmer, by repeatedly passing it along to whoever uses the API.

 

 

*Actually in pure OO (Java, C#, Objective-C) LGNUM, LGTYP and DATUM would be classes, but let's take it one step at a time.

Currently i'm working in SAP CS implementing a mobile solution for Quality Assurance based on SAP CS Workorders.

 

One of my requirements was to store information on the order header of this CS order.

 

Looking at the function module BAPI_ALM_ORDER_MAINTAIN it has importing parameters allowing you to put in text:

 

text-tables.png

But soon I discovered that these text weren't showing up in the actual order header nor in the operations linked to the order.

 

So I needed a different approach, luckily SAP also provides us with SAVE_TEXT and READ_TEXT function modules.

 

I implemented a method saving the text for me:

 

method.png

 

Soon I discovered that the text I was changing using this function module turned up to be in the table STXH but still not showing up in IW33.

 

Therefore I debugged the logic behind the transaction itself and came to the conclusion that the longtext indicator on the order header (AUFK-LTEXT) wasn't properly used.

 

When looking at the domain value of the longtext it only allows true or false (X or blank):

 

longtext yes-no.png

But I found that iw32 was updating the field with the language key:

 

longtext N.png

 

Therefore, against everything I've been taught I added an additional line of code updating the longtext indicator directly:

 

update dbtab.png

 

This actually shows the longtext in iw33.

 

order.png

Do you ever have this problem?  You're in the middle of writing a fantastic ABAP program.  It hums, it purrs, it looks reeeeally good.  The code is so clean uncle Bob would be proud.  You use the best design patterns and your application is modularized well.  Then you realize you need to log a few lines of text to view later in SLG1.  Your heart sinks.  Your robust, clean application is about to become polluted by countless 10-line calls to FM 'BAL_LOG_CREATE' and its fellow hoodlums, all because you wanted to save the output of a BAPI you called.  First world problems, AMIRITE?  Anyone?


The obvious solution is to put an object-oriented wrapper around these functions.  This solution is so obvious, in fact, that every department in SAP has written their own verison of it, and my company has added two more.  This is consistent with SAP's approach to developer tools: Quantity Guaranteed.


So why do we need another OO wrapper?  Well, because I believe in the power of collaboration, for one.  None of the other logs have been designed by the developer community.  Another reason is that logs have come a long way since 1999, when SAP released their function modules for the application log, and developers are used to a more concise syntax.  For instance, if I want to write to the console in javascript, it's:


console.log('The System is Down');


but in SAP, I have to declare 2 variables, then spend a page of code calling function modules BAL_LOG_CREATE, BAL_LOG_MSG_ADD, and BAL_DB_SAVE.  Part of the reason is that SAP has multiple logs, while a web browser only logs messages in one console.  So when you log anything in SAP, it must go to a specific application log object and sub-object.  Android (java) also writes to multiple logs.  Its logs are called with two arguments, tag and message, like:


Log.e("SystemMsgs", "The system is Down");


If logging in ABAP was going to be just as awesome (and I know it can be), what would it look like?  Please post your ideas and discuss!  Once enough good ideas are gathered, I'd like to start building something on github.  Anyone is welcome to contribute code or just ideas and insights.  Here's an example what I think would make a good application log API:


log = zcl_app_log=>get( object = 'ZFICO' subobject = 'INTERFACES' ).

TRY.

    zcl_accounting=>do_some_important_upload( ).

    log->add( 'The interface has finished' ).

  CATCH zcx_upload_failed INTO err.

    log->add( err ).

ENDTRY.

Recently I had a situation where I needed to update MIGOs defaults programatically. I tried to search for a solution but couldn't see it mentioned anywhere and hence decided to write it down . I hope that it'll be useful for anyone in a similar situation.

 

Background:


MIGO is a multi-purpose transaction which can be used for various activities in material management - performing goods movements, receipt of purchase order etc. It replaces many older transactions . There are others as well but to list down a few:

 

  • Post a goods receipt to a known purchase order (transaction MB01)
  • Change a material document from goods receipts (transaction MB02)
  • Display a material document from goods receipts (transaction MB03)
  • Cancel a material document from goods receipts (transaction MBST)

 

To understand why we may need to ever need to modify MIGO defaults, consider the below situation.

 

- A file arrives for processing in SAP ( say as a batch job ) .

 

- Initial goods movement is tried using a BAPI. If the processing fails , a user SM35 BDC session is created for processing which can be reprocessed by users ( the users are familiar with MIGO ). A custom transaction for reprocessing is created as the users couldn't be given access to SM35. SM35 allows you to display and potentially reprocess all sessions which can be tricky if the authorisations are a bit leaky.

 

1.png

 

 

The failed sessions can then be processed by a custom transaction - the SM35 sessions are persisted to a temporary table to be used by reprocessing transaction.

 

Problem:

 

Everything looks good in theory : all automatic postings are done in background and any errors can be handled by the custom transaction. However, while the user is performing the reprocessing the MIGO session, something interesting happens - if the user opens a parallel MIGO session and performs some other processing in parallel, the subsequent sessions start to fail in the custom transaction. Users could be processing multiple sessions sequentially and might go away and do some other movements in parallel in MIGO.

2.png

 

Why does this happen ?

 

MIGO stores user's defaults trying to optimise the usage so that the user doesn't have to set the selections - this causes the defaults to be always set whenever you use the transaction. The parallel session which the user opened has overridden the defaults and as a result, subsequent failed sessions have different default values set in the screen even though the BDC recording used in SM35 session was set correctly. User defaults is overriding BDC set values .

 

 

Looking at the below image, the BDC session has set values A07 and R10 for action and sub-selection within action.

MIGO3.png

 

However, if the user choses something else in a parallel session ( say A05 and a sub-selection ) , it overrides the action default and subsequent SM35 sessions start failing as then MIGO will start with A05 / sub-selection.

 

Solution:

 

MIGO stores user's defaults in table ESDUS and these defaults correspond to MIGO_FIRSTLINE action. Seeing the below table entries, the settings for the user are:

 

Default is action = A07

and sub-selection for A07 is R10.

 

Hence, A07 / R10 will appear for ACTION and sub-selection ( as shown in above image ) .

 

MIGO4.png

Show Me The Code:

 

Now, we know where they're stored, how to update them ?

 

Class CL_MMIM_USERDEFAULTS can be used to  read and update the parameters. It's a singleton and hence there should be only instance at a given time. Consequently, if we're using it we have to ensure the instance is destroyed . This is achieved by FLUSH_ALL method of the class. Above methods are self explanatory and the constructor requires ACTION value.

 

MIGO5.png

 

So I did the following:


- Instantiate the class using ACTION as "MIGO_FIRSTLINE" and set the instance values.


- Set the values:


o_migo_defaults->set

                                         ( i_element = 'ACTION'
                      i_active
= lv_action      ).

- Flush the value to dB and destroy the instance


o_migo_defaults->flush( ). "Save values to dB
o_migo_defaults
->flush_all( ). "Destroy this instance as others instance will start producing errors


The table has other values used in MIGO defaults ( e.g. default movement type for an action ) and can be similarly updated.

There are several use cases when you need to convert SOLIX table to XSTRING. For example, when you are using sap gateway for downloading files from SAP office, you will need to use conversion from solix to xstring.

 

You can use method cl_bcs_convert=>solix_to_xstring or FM  SCMS_BINARY_TO_XSTRING for this conversion.

 

Now, the issue: SOLIX is a table of RAW 255, that means every line of this table has 255 raw bytes. When you don't specify output length of XSTRING variable, empty raw bytes from the last line are converted as well which means that you will supply corrupted file. It is be readable in most cases, but for example Microsoft Word will show warning message that the file is corrupted, though it can be opened afterwards.

 

Solution is to set number of bytes that needs to be converted (actual file size, not lines of table multiplied by size of line) as can be seen in following example:

 

    DATA:
      lt_file      TYPE solix_tab,
      lv_length   TYPE i,
      ls_doc_data TYPE sofolenti1.

    CALL FUNCTION 'SO_DOCUMENT_READ_API1'
      EXPORTING
        document_id                = iv_id
      IMPORTING
        document_data             = ls_doc_data
      TABLES
        contents_hex               = lt_file
      EXCEPTIONS
        document_id_not_exist      = 1
        operation_no_authorization = 2
        x_error                    = 3
        OTHERS                     = 4.
    IF sy-subrc = 0.
      lv_length = ls_doc_data-doc_size.

      IF lt_att IS NOT INITIAL.
        CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
          EXPORTING
            input_length = lv_length
          IMPORTING
            buffer       = ev_xstring_content
          TABLES
            binary_tab   = lt_file
          EXCEPTIONS
            failed       = 1
            OTHERS       = 2.
      ENDIF.
    ENDIF.

Here is an example of creating  word documents from templates stored in web repository (SMW0) using wordprocessingml.

A very simple form can be done in around an hour of time.

 

USE CASE

    Prepare the template that needs to be filled with data.

Exmaple 1.png

 

Write the program that will fill the document.

 

  data: LO_FORM type ref to Z_MWL_FORM.

 

   data: L_TEMPLATE type STRING value 'block'.

   data: L_INDEX type I value 1.

   data: L_NUM type I.

 

   create object LO_FORM

     exporting

       I_TEMPLATE = 'Z_TEST'.

 

 

   LO_FORM->REPLICATE(

    I_TEMPLATE_ID = L_TEMPLATE

    I_COPY_NUM = 2

   ).

 

 

   LO_FORM->PREP_SEQ_ACCESS( ).

 

   if LO_FORM->FIND_VARIABLE( 'name' ) eq ABAP_TRUE.

     LO_FORM->SET_VALUE( 'Zhambyl').

   endif.

 

   if LO_FORM->FIND_VARIABLE( 'age' ) eq ABAP_TRUE.

     LO_FORM->SET_VALUE( '25' ).

   endif.

 

   if LO_FORM->FIND_VARIABLE( 'gender' ) eq ABAP_TRUE.

     LO_FORM->SET_VALUE( 'male' ).

   endif.

 

   do 2 times.

 

     if LO_FORM->FIND_BLOCK( L_INDEX ) eq ABAP_TRUE.

       if LO_FORM->FIND_VARIABLE( 'var1' ) eq ABAP_TRUE.

         LO_FORM->SET_VALUE( '1' ).

       endif.

 

       if LO_FORM->FIND_VARIABLE( 'var2' ) eq ABAP_TRUE.

         LO_FORM->SET_VALUE( '2' ).

       endif.

 

       if LO_FORM->FIND_VARIABLE( 'var3' ) eq ABAP_TRUE.

         LO_FORM->SET_VALUE( '3' ).

       endif.

 

     endif.

     add 1 to L_INDEX.

   enddo.

 

   LO_FORM->FINISH_SEQ_ACCESS( ).

 

   LO_FORM->CLEAN(  ).

   LO_FORM->DISPLAY( ).

 

Open the filled document on client machine.

Example 2.png

 

 

Implemantaion details

 

class Z_MWL_FILE definition

  public

   create public .

 

  public section.

 

     data EXTENSION type STRING .

     data TEMPDIR type STRING .

     data BSTRING type XSTRING.

 

     methods: DOWNLOAD " download file from web repository

       importing

         VALUE(I_TEMPLATE) type STRING

          returning

         VALUE(R_SUBRC) like SY-SUBRC.

 

     methods GET_BSTRING " returns xstring representation of file

       returning

         VALUE(R_STRING) type XSTRING.

 

     methods GET_TEMP_DIR " chose file storage location

       returning

         VALUE(R_PATH) type string.

 

     methods SAVE_ON_FRONTEND " upload file to client

       importing

         VALUE(I_STRING) type XSTRING

       returning

         VALUE(R_SUBRC) like SY-SUBRC.

 

   protected section.

   private section.

 

ENDCLASS.

 

 

 

CLASS Z_MWL_FILE IMPLEMENTATION.

 

   method DOWNLOAD.

 

     data: LS_KEY type WWWDATATAB.

     data: LS_MIME type W3MIME.

     data: LT_MIME type standard table of W3MIME.

     field-symbols &lt;LFS_DATA> type ANY.

 

     LS_KEY-RELID = 'MI'.

     LS_KEY-OBJID = I_TEMPLATE.

 

     call function 'WWWDATA_IMPORT'

       exporting

         KEY               = LS_KEY

       tables

         MIME              = LT_MIME

       exceptions

         WRONG_OBJECT_TYPE = 1

         IMPORT_ERROR      = 2

         others            = 3.

 

     if SY-SUBRC eq 0.

       loop at LT_MIME into LS_MIME.

         assign LS_MIME to &lt;LFS_DATA> casting type ('X').

         if &lt;LFS_DATA> is assigned.

           concatenate BSTRING &lt;LFS_DATA> into BSTRING in byte mode.

           unassign &lt;LFS_DATA>.

         endif.

       endloop.

     else.

       R_SUBRC =  SY-SUBRC.

     endif.

 

   endmethod.                    "DOWNLOAD

 

   method GET_BSTRING.

     R_STRING = BSTRING.

   endmethod.                    "GET_BSTRING

 

  method GET_TEMP_DIR.


     data: L_WTITLE type STRING.

     data: L_NAME type STRING.

     data: L_FPATH type STRING.

 

     L_WTITLE = 'CHOSE FILE STORAGE LOCATION'.

 

     CL_GUI_FRONTEND_SERVICES=>FILE_SAVE_DIALOG(

           exporting

             WINDOW_TITLE = L_WTITLE

             DEFAULT_EXTENSION = 'docx'

             FILE_FILTER = 'docx'

           changing

             FILENAME = L_NAME

             PATH = TEMPDIR

             FULLPATH = L_FPATH ).

 

     CL_GUI_CFW=>FLUSH( ).

     R_PATH = L_FPATH.

 

   endmethod.                    "GET_TEMP_DIR

 

  method SAVE_ON_FRONTEND.

     data: LV_FILE_TAB     type standard table of SOLISTI1,

           LV_BYTECOUNT    type I.

     data: L_FPATH type STRING.

     call function 'SCMS_XSTRING_TO_BINARY'

       exporting

         BUFFER        = I_STRING

       importing

         OUTPUT_LENGTH = LV_BYTECOUNT

       tables

         BINARY_TAB    = LV_FILE_TAB.

     "Save the file

 

     L_FPATH = GET_TEMP_DIR( ).

     if L_FPATH is not initial.

       CL_GUI_FRONTEND_SERVICES=>GUI_DOWNLOAD(

         exporting

           BIN_FILESIZE = LV_BYTECOUNT

           FILENAME     = L_FPATH

           FILETYPE     = 'BIN'

         changing

           DATA_TAB     = LV_FILE_TAB

       ).

       if SY-SUBRC ne 0.

         R_SUBRC = SY-SUBRC.

       endif.

     else.

       R_SUBRC = 2.

     endif.

  endmethod.                    "SAVE_ON_FRONTEND

ENDCLASS.


Secondly we create main class that is used to manipulate the word document.

This class relies on classes found in packages: S_OOXML_CORE, SXML and XSLT transformations.

 

class Z_MWL_FORM definition

   public

   create public .

 

   public section.

     data: MAIN_PART type XSTRING .

     data: DOCUMENT type XSTRING .

     data: INTRM_PART type XSTRING .

     data: FINAL_DOC type XSTRING.

 

     methods CONSTRUCTOR importing I_TEMPLATE type STRING. " Finds the main part of word document from zip pakcage container and

                                                                                                    " stores it. I_TEMPLATE is the logical name of the file in smw0

    methods DISPLAY.                     " This method packages updated main part and uploads it to front-end. Dont mind the name.

 

     methods REPLICATE                  " Replicates marked block of text using transformations and substitues standard markups for custom ones

      importing I_TEMPLATE_ID type STRING

                I_COPY_NUM type I.

 

     methods: FIND_VARIABLE  " Finds tag named variable using sxml. I_var is a value for name attribute of this tag.

               importing I_VAR type STRING

              returning VALUE(RV_FOUND) type ABAP_BOOL.

 

     methods: FIND_BLOCK importing I_BLOCK type           " Finds tag named block using sxml. I_block is a value for number attribute of this tag.

              returning VALUE(RV_FOUND) type ABAP_BOOL.     " Block contains several variables that can be copyed with different block numbers

 

    methods: SET_VALUE importing I_VAL type STRING.      " Replaces value of placeholder variable

 

     methods: PREP_SEQ_ACCESS.    " Converts xstring to Xml objects and prepares them for sequencial access

     methods: FINISH_SEQ_ACCESS.  " Converts from sXML back to xstring representation

     methods CLEAN.                           " Clear's all the custom mark up from main part of word document

   protected section.

     data: O_FILE               type ref to ZCL_ZK_MWL_FILE.

     data: O_DOC               type ref to CL_DOCX_DOCUMENT.

     data: O_DOCUMENTPART       type ref to CL_DOCX_MAINDOCUMENTPART.

 

     data: O_SREADER type ref to IF_SXML_READER.

     data: O_SWRITER type ref to IF_SXML_WRITER.

     data: O_SNODE  type ref to IF_SXML_NODE.

     data: O_SVALUE_NODE  type ref to IF_SXML_VALUE_NODE.

   private section.

 

ENDCLASS.

 

 

 

CLASS Z_MWL_FORM IMPLEMENTATION.

 

   method CLEAN.

     if INTRM_PART is not initial.

       call transformation Z_CLEAN

       source xml INTRM_PART

       result xml FINAL_DOC.

     endif.

   endmethod.                    "CLEAN

 

   method CONSTRUCTOR.

     create object O_FILE.

     O_FILE->DOWNLOAD( I_TEMPLATE ).

     DOCUMENT = O_FILE->GET_BSTRING( ).

     try.

         O_DOC = CL_DOCX_DOCUMENT=>LOAD_DOCUMENT( IV_DATA = DOCUMENT ).

* get the maindocument part

         O_DOCUMENTPART = O_DOC->GET_MAINDOCUMENTPART( ).

         MAIN_PART = O_DOCUMENTPART->GET_DATA( ).

 

       catch CX_OPENXML_FORMAT.

       catch CX_OPENXML_NOT_ALLOWED.

       catch CX_OPENXML_NOT_FOUND.

       catch CX_TRANSFORMATION_ERROR.

     endtry.

   endmethod.                    "constructor

 

   method DISPLAY.

     if FINAL_DOC is not initial.

       O_DOCUMENTPART->FEED_DATA( FINAL_DOC ).

     elseif MAIN_PART is not initial.

       O_DOCUMENTPART->FEED_DATA( MAIN_PART ).

     endif.

     FINAL_DOC = O_DOC->GET_PACKAGE_DATA( ).

     if O_FILE->SAVE_ON_FRONTEND( FINAL_DOC ) ne 0.

       message 'Выгрузка отменена' type 'S'.

     endif.

   endmethod.                    "Display

 

   method FIND_BLOCK.

     data: LX_ROOT type ref to CX_SXML_ERROR.

     data: LO_OPELEM type ref to IF_SXML_OPEN_ELEMENT.

     data: L_AT_VAL type ref to IF_SXML_VALUE.

     data: L_VAL type STRING.

 

     if O_SREADER is bound and O_SWRITER is bound.

 

       while RV_FOUND ne ABAP_TRUE.

         try.

             O_SNODE = O_SREADER->READ_NEXT_NODE( ).

             if O_SNODE is initial.

               exit.

             endif.

             if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_ELEMENT_OPEN.

               LO_OPELEM ?= O_SNODE.

               if LO_OPELEM->IF_SXML_NAMED~QNAME-NAME eq 'block'.

                 L_AT_VAL = LO_OPELEM->GET_ATTRIBUTE_VALUE( 'num' ).

                 L_VAL = L_AT_VAL->GET_VALUE( ).

                 if L_VAL eq I_BLOCK.

                   RV_FOUND = ABAP_TRUE.

                 endif.

               endif.

             endif.

             O_SWRITER->WRITE_NODE( O_SNODE ).

           catch CX_SXML_ERROR into LX_ROOT.

             exit.

         endtry.

       endwhile.

     endif.

   endmethod.                    "FIND_BLOCK

 

   method FIND_VARIABLE.

     data: LX_ROOT type ref to CX_SXML_ERROR.

     data: LO_OPELEM type ref to IF_SXML_OPEN_ELEMENT.

     data: L_AT_VAL type ref to IF_SXML_VALUE.

     data: L_VAL type STRING.

 

     if O_SREADER is bound and O_SWRITER is bound.

 

       while RV_FOUND ne ABAP_TRUE.

         try.

             O_SNODE = O_SREADER->READ_NEXT_NODE( ).

             if O_SNODE is initial.

               exit.

             endif.

             if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_ELEMENT_OPEN.

               LO_OPELEM ?= O_SNODE.

               if LO_OPELEM->IF_SXML_NAMED~QNAME-NAME eq 'variable'.

                 L_AT_VAL = LO_OPELEM->GET_ATTRIBUTE_VALUE( 'mark' ).

                 L_VAL = L_AT_VAL->GET_VALUE( ).

                 if L_VAL eq I_VAR.

                   RV_FOUND = ABAP_TRUE.

                 endif.

               endif.

             endif.

             O_SWRITER->WRITE_NODE( O_SNODE ).

           catch CX_SXML_ERROR into LX_ROOT.

             exit.

         endtry.

       endwhile.

 

       clear RV_FOUND.

 

       while RV_FOUND ne ABAP_TRUE.

         try.

             O_SNODE = O_SREADER->READ_NEXT_NODE( ).

             if O_SNODE is initial.

               exit.

             endif.

             if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_ELEMENT_OPEN.

               LO_OPELEM ?= O_SNODE.

               if LO_OPELEM->IF_SXML_NAMED~QNAME-NAME eq 't'.

                 RV_FOUND = ABAP_TRUE.

               endif.

             endif.

             O_SWRITER->WRITE_NODE( O_SNODE ).

           catch CX_SXML_ERROR into LX_ROOT.

             exit.

         endtry.

       endwhile.

 

       clear RV_FOUND.

 

       while RV_FOUND ne ABAP_TRUE.

         try.

             O_SNODE = O_SREADER->READ_NEXT_NODE( ).

             if O_SNODE is initial.

               exit.

             endif.

             if O_SNODE->TYPE eq IF_SXML_NODE=>CO_NT_VALUE.

               O_SVALUE_NODE ?= O_SNODE.

               RV_FOUND = ABAP_TRUE.

               exit.

             endif.

             O_SWRITER->WRITE_NODE( O_SNODE ).

           catch CX_SXML_ERROR into LX_ROOT.

             exit.

         endtry.

       endwhile.

     endif.

   endmethod.                    "FIND_VARIABLE

 

   method FINISH_SEQ_ACCESS.

     data: LX_ROOT type ref to CX_SXML_ERROR.

     data: LO_WRITER type ref to CL_SXML_STRING_WRITER.

     if O_SREADER is not initial and O_SWRITER is bound.

       do.

         try.

             O_SNODE = O_SREADER->READ_NEXT_NODE( ).

             if O_SNODE is initial.

               exit.

             endif.

             O_SWRITER->WRITE_NODE( O_SNODE ).

           catch CX_SXML_ERROR into LX_ROOT.

             exit.

         endtry.

       enddo.

 

       try.

 

           LO_WRITER ?= O_SWRITER.

           INTRM_PART = LO_WRITER->GET_OUTPUT( ).

         catch CX_SXML_ERROR into LX_ROOT.

           exit.

       endtry.

     endif.

   endmethod.                    "FINISH_SEQ_ACCESS

 

   method PREP_SEQ_ACCESS.

     if INTRM_PART is not initial.

       O_SREADER ?= CL_SXML_STRING_READER=>CREATE( INTRM_PART ).

       O_SWRITER ?= CL_SXML_STRING_WRITER=>CREATE( ).

     endif.

   endmethod.                    "prep_seq_access

 

   method REPLICATE.


     if INTRM_PART is initial.

       call transformation Z_REPLICATE

       source xml MAIN_PART

       result xml INTRM_PART

       parameters TEMPLATE_ID = I_TEMPLATE_ID

                  COPY_NUM = I_COPY_NUM.

     else.

 

       call transformation Z_REPLICATE

       source xml INTRM_PART

       result xml INTRM_PART

       parameters TEMPLATE_ID = I_TEMPLATE_ID

                  COPY_NUM = I_COPY_NUM.

     endif.

   endmethod.                    "replicate

 

 

  method  SET_VALUE.

     data: LX_ROOT type ref to CX_SXML_ERROR.

     data: L_XSTRING type XSTRING.

 

*  L_XSTRING = CL_ABAP_CODEPAGE=>CONVERT_TO( I_VAL ).

 

     if O_SVALUE_NODE is bound and O_SWRITER is bound.

       try.

           O_SVALUE_NODE->IF_SXML_VALUE~SET_VALUE( I_VAL ).

           O_SWRITER->WRITE_NODE( O_SNODE ).

         catch CX_SXML_ERROR into LX_ROOT.

           exit.

       endtry.

     endif.

  endmethod.                    "set_value

ENDCLASS.


Following are the transformations used in the class described above.


Transformation z_replicate


<xsl:transformxmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:sap="http://www.sap.com/sapxsl"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" version="1.0">
   <xsl:output encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="no" standalone="yes"/>
   <xsl:param name="TEMPLATE_ID"/>
   <xsl:param name="COPY_NUM" sap:type="number"/>
   <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
   </xsl:template>
   <xsl:template match="w:sdt">
     <xsl:choose>
       <xsl:when test="descendant::w:tag[@w:val=$TEMPLATE_ID]">
         <xsl:call-template name="multiply">
           <xsl:with-param name="maxCount" select="$COPY_NUM"/>
           <xsl:with-param name="nodeToCopy" select="."/>
         </xsl:call-template>
       </xsl:when>
       <xsl:otherwise>
         <xsl:element name="variable">
           <xsl:attribute name="mark">
             <xsl:value-of select="descendant::w:tag/@w:val"/>
           </xsl:attribute>
           <xsl:apply-templates select="w:sdtContent/node()|@*" mode="variable"/>
         </xsl:element>
       </xsl:otherwise>
     </xsl:choose>
   </xsl:template>
   <xsl:template name="multiply">
     <xsl:param name="maxCount"/>
     <xsl:param name="i" select="1"/>
     <xsl:param name="nodeToCopy"/>
     <xsl:choose>
       <xsl:when test="$i &lt;= $maxCount">
         <xsl:element name="block">
           <xsl:attribute name="num">
             <xsl:value-of select="$i"/>
           </xsl:attribute>
           <!--          <xsl:copy-of select="$nodeToCopy/w:sdtContent/node()|@*"/>-->
           <xsl:apply-templates select="$nodeToCopy/w:sdtContent/node()|@*"/>
         </xsl:element>
         <xsl:call-template name="multiply">
           <xsl:with-param name="maxCount" select="$maxCount"/>
           <xsl:with-param name="nodeToCopy" select="$nodeToCopy"/>
           <xsl:with-param name="i" select="$i+1"/>
         </xsl:call-template>
       </xsl:when>
       <xsl:otherwise/>
     </xsl:choose>
   </xsl:template>
   <xsl:template match="node()|@*" mode="variable">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*" mode="variable"/>
     </xsl:copy>
   </xsl:template>
   <xsl:template match="w:sdtContent/w:r[1]" mode="variable">
     <xsl:copy>
        <xsl:apply-templates select="node()|@*" mode="variable"/>
     </xsl:copy>
   </xsl:template>
    <xsl:template match="w:sdtContent/w:r[position() != 1]" mode="variable">
   </xsl:template>
</xsl:transform>



Transformation z_clean

<xsl:transformxmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:sap="http://www.sap.com/sapxsl"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" version="1.0">
   <xsl:output encoding="UTF-8" indent="yes" method="xml" omit-xml-declaration="no" standalone="yes"/>
   <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
   </xsl:template>
   <xsl:template match="variable">
       <xsl:apply-templates select="node()|@*"/>
   </xsl:template>
   <xsl:template match="block">
       <xsl:apply-templates select="node()|@*"/>
   </xsl:template>
</xsl:transform>




I know this may not be universally agreed upon, but I think it's something worthy of being discussed and so I decided to explain my reasoning behind it.

Although I started my programming career in ABAP over the years I've programmed in Java, C#, Objective-C,etc. In these languages an object has a name, and a fully qualified name:

 

  • Name: Delivery
  • Fully Qualified Name: com.sd.litigations.delivery

 

The benefit of this structure is that the "litigations" team doesn't have ownership on the usage of the "delivery" word, like a trademark. There can be another object called delivery in another package like "com.sd.whatever".

 

In ABAP you have something similar, the custom namespaces, which you may have seen in SAP developments /<application>/ but are rarely used outside SAP (I've used this once in a project). Most of the times we all share the Z or Y namespace.

 

So In my arrogance I create a package called ZLITIGATIONS, go into SE24, and claim the name ZCL_DELIVERY for myself (like I've seen people do so many times....). Why should I be encouraged to do this? Is my litigations application more important that some other package? No.

 

That's why in my projects we use some sort of package symbol in the class name, like ZLT_DELIVERY. This way someone else can create a DELIVERY object without it feeling like a "second best", besides making it easier to search for the class with intelisense (in ABAP for Eclipse).

 

PS: I know some will question why there would be two delivery classes in the first place, but let's keep that for another discussion. Think of another less widely used object, the same reasoning applies.

Hello guys

 

I am trying to get a hand of the patterns in ABAP and how to apply them.

 

I hope this is going to be one of the many posts i will do on the Patterns

 

As we write more code things get so much similar and what i learnt from reading Java design Patterns

 

 

Keep your code open for extension and closed for modification as much as possible!!

 

 

MVC Design Pattern

 

In Model view controller the main goal is:

to be able to loose couple the presentation layer from the backend layer!!

 

  • View is how i get input from user and display output
  • Model is my Business data
  • Controller is the manager between these two

There is always two components in a ABAP report:

 

Lets say an ALV  report

 

  • Selection screen(Presentation layer)
  • Data layer( Fetching of the data-SQL queries etcc-Model layer)
  • Displaying the data(View Layer)

 

So we could divide the code into 3 pieces Model-View-Controller

 

 

Controller might not be very necessary for small reports.

 

having controller is a good approach as this will be like a mediator between the model and the view

 

 

So what are the don'ts:

  • View should never know much about the model
  • Model should never be referencing the vie

Here is an example:

 

Lets say that you write a report and this report users should be able to display the data as an ALV or as a smartforms or even in pdf format!!

 

This is a great example of MVC as the view layer will be changing like a skin as most of the apps use these days even a web browser

 

Here is the code sample

 

Lets start with the example ALV that will display sales order data from tables vbak and vbap

 

VIEW

 

Define an abstract view class where you can extend it to many different types of views!!

 

***Display

class lcl_view DEFINITION ABSTRACT .

  PUBLIC SECTION.

    METHODS: display  ABSTRACT CHANGING it_data TYPE STANDARD TABLE.

ENDCLASS.

 

class lcl_view_alv  DEFINITION INHERITING FROM lcl_view .

  PUBLIC SECTION.

    METHODS: display REDEFINITION.

endclass.

 

****Smartforms view

CLASS lcl_view_smartforms DEFINITION INHERITING FROM lcl_view.

  PUBLIC SECTION.

    METHODS: display REDEFINITION.

ENDCLASS.

 

****PDF view

CLASS lcl_view_pdf DEFINITION INHERITING FROM lcl_view.

  PUBLIC SECTION.

    METHODS: display REDEFINITION.

ENDCLASS.

 

 

 

So now we have 3 diffrent types of views!!

 

 

MODEL

 

 

Model is the business data that we will be getting

 

We could also make model an abstract class and then extend it but for simplicity not now!!

here is an example:

 

 

 

*****DATA Layer

class lcl_model DEFINITION.

  PUBLIC SECTION.

    TYPES: BEGIN OF ty_out,

      vbeln type vbap-vbeln,

      posnr type vbap-posnr,

      matnr type vbap-matnr,

      vkorg type vbak-vkorg,

      END OF ty_out

      .

    TYPES: tt_out TYPE STANDARD TABLE OF ty_out.

    TYPES: gr_vbeln TYPE RANGE OF vbap-vbeln.

    DATA: gt_output TYPE tt_out.

 

    methods: constructor,

              select_data IMPORTING VALUE(rs_vbeln) TYPE gr_vbeln.

    .

  ENDclass.

 

 

CONTROLLER

Controller needs to know about both the view and the model

so that it can notify

The responsibility is with the controller to manage the tasks !!

 

 

class lcl_controller DEFINITION.

  PUBLIC SECTION.

    METHODS: constructor IMPORTING io_view_type TYPE CLIKE OPTIONAL.

    methods: get_data IMPORTING ir_vbeln TYPE lcl_model=>GR_VBELN.

    methods: set_view IMPORTING io_view_type TYPE REF TO lcl_view.

    methods: main IMPORTING  ir_vbeln TYPE lcl_model=>GR_VBELN

                              VALUE(iv_view_type) TYPE string.

    methods: display.

  PROTECTED SECTION.

    DATA: lo_view type REF TO lcl_view.

    DATA: lo_model type REF TO lcl_model.

ENDCLASS.

 

 

SUMMARY

You need to define a loose coupled application and in the future when you need a new View all you need is write a new one extending the abstract view class!!

 

MVC is a great pattern and design and get out there to try as much as you can!!

 

 

All the code i provide is attached see below

 

Sample code: is in text format as well as Saplink format can be provided if you request on <email address removed by moderator> as a nugget!!

i couldnt attach the nugget into this failed somehow!!

Suppose there are 3 Master Pages


Three pages are created in Master Page in Adobe Form:-


Page 1--Cover Page, fix will not repeat.

Page 2--Repeat based on the Item level data with all header data on every page.

Page 3--Repeat based on the Item level data with all header data on every page.


I have drag the table from the data view in Subform_Page2 and Subform_Page3.


Problem:The Column header was coming only on the first page of Page 2 and Page 3 and for the subsequent pages of Page 2 and Page 3 the column header was not getting repeated.


Solution:


Hierarchy:-

 

. Hierarchy.JPG

 

Page 2---->Subform_Page2                                                                                                   

      Subform_Page2.JPG

Repeat Column Header (Name , City , Address , State , City 1 , Address1 and City1 ) in every page which is under "OverflowLeader" 

Column Header.JPG

Properties of OverflowLeader : Row :

Column Header_Row.JPG

OverflowLeader: Pagination :


Do as mentioned below :


1. In Place = Click on icon and then Select Top of Content Area---->Choose the Content Area of the Subform_Page2

2..Check the check box " Include Header Row in Initial Page ".

3  Check the check box " Include Header Row in Subsequent Changes".

Column Header_Pagination.JPG

OverflowLeader: Binding :


Do as mentioned below:

1. Check the check box "Repeat  Row for each Data Item.

2..Check the Min Count and add 1.

Column Header_Binding.JPG

I have followed the same steps for Subform_Page3.

Doing the above mentioned steps the Column Header is getting repeated on every page and not only on the first page of Page 2 and Page 3.

 

Thanks.

Shweta

I'm writing this blog after reading Understanding Widening Cast in ABAP Objects since it became clear to me that the difference between reference types and object type is not clear for many SCN users who are not used to Object Oriented Programming. Knowing the difference between the two is critical to understanding what you can do with casting and how powerful the concept of inheritance is.

 

I'll start by giving an example of why narrowing cast is so important. Imagine the following scenario, where the specific fruits are children of the super class Fruit:

 

inheritance.png

 

You want to create a table of fruit, then loop at it, and write the name of the fruit to the screen. The required data should can be declared as:

 

DATA: lt_fruits TYPE TABLE OF REF TO ZCL_FRUIT,
            lo_fruit  TYPE  REF TO ZCL_FRUIT,
            lo_mango TYPE REF TO ZCL_MANGO,
            lo_apple  TYPE REF TO ZCL_APPLE,
            lo_orange TYPE REF TO ZCL_ORANGE.



And then you do something like:

 

lo_mango = new ZCL_MANGO( ).
lo_fruit ?= lo_mango.
append lo_fruit to lt_fruit.



 

This is where the difference between reference type and object type becomes critical.

  • The object type is intrinsic to the class of the constructor (new ZCL_MANGO) used to bring it to "life". Think of the object as memory space that contains information, whose type never changes after it is instantiated.
  • The reference type is the type of the pointer (in this case lo_mango and lo_fruit) to the memory space. It's your gateway, your "API",  only through them can you access the memory (the intrinsic object, whose type was determined by the constructor).

 

When I make a cast from lo_mango to lo_fruit, the object itself and therefore its type remains the same. Same variables, same type,  nothing changes except the type of the pointer, the reference. As long as we use a reference of the super class type we only have access in the code to the attributes and methods of the superclass, but that doesn't mean the attributes of the original object were lost. They are still there waiting!

 

This dichotomy is very important, because it allows us to keep similar objects together, in the same table for example, while keeping their intrinsic properties intact. For example lets assume that for the very specific case of the Apple we want to output the apple's type besides the name of the fruit, the code would be something like:

 

Loop at lt_fruit into lo_fruit.
     write lo_fruit->get_name( ).
     if cl_abap_classdescr=>get_class_name( lo_fruit ) = 'ZCL_APPLE'.
          lo_apple ?= lo_fruit.
          write lo_apple->get_type_of_apple( ).
     endif.
Endloop.





 

It should be become even clearer by the usage of cl_abap_classdescr=>get_class_name( lo_fruit ) and the fact that it returns ZCL_APPLE (instead of ZCL_FRUIT), that indeed the object retains all the attributes that were given to him by the constructor, even if the reference is of the super class type.

 

Now imagine a scenario where casting didn't exist, and the code you would need. You would need 3 tables, 3 loops. Now expand this to a real program, inheritance allow much more elegant coding.

Hi ,

I am writing this blog due to the reason, when I search in google , I didn't get proper response

 

If you encounter dump in ALV when click on some ICON, you will get this dump. TO Resolve this you need to create FINAL INternal table structure with Ref. Feilds and not the Domain fields.

 

I mean --> TYPES Declaration :      BMEIN     TYPE BASME" Base Unit  , Instead you need to use

                                                          BMEIN     TYPE MEINS" Base Unit ( MEINS is Ref Field ).

 

With Regards,

Bala M

Hi all,

today's blog is a deeper insight in “The 4th point: think about your developing twice” published in Some recommended Points everybody should remember when developing (ABAP) - featured title "Be a better developer"

 

As a quality manager it is also part of my daily work to share knowledge and to onboard new employees. Out of that I have a lot of different views from people which aren't much influenced. That means I get a lot of different views to code quality and how the different people think about it.

A funny thing is, when I tell them, that I’m part of the quality team, a lot of them are saying that this isn't effective or even they got not that good stories to tell. A lot are saying it is very abstract.

In other words they are saying that this is too far away from the daily work and it is not helping them to improve the code quality.

That’s why I thought, I will share ten facts about developing things.

( Be careful, it is not doing your work and maybe you need more time to get it done )

 

1st point Functionality

Does your code do what you want to?

Of course, everybody of us is doing that in a way, but did you ever thought about it before starting developing?

I mean all the stuff you discovered before starting developing saves you time. So take your time and find your spots to implement your additional coding. If you have a Greenfield development you might save more time by drawing a class diagram with all the relations between, but that is another story to tell

 

 

 

2nd point software reliability

Does your code affect other processes?

Make sure, that your code is not affecting other processes. Of course, if you answered point one with yes, you might have already checked this too, but just because most of us implement that in the first mentioned  point, it is an extra point and I need to mention it.

 

3rd point usability

Would a user understand it easily?

That is a really big point and no, this is not your business. It is your business and I tell you why. We develop the things and we also need to take care about the usability. A simple example:

You see, take your time and think about your screens you develop, no matter which technique you are using. Just because all necessary results show up on the screen doesn't mean that it is a good program at all.

 

4th point efficiency

What about the run duration of my program?

This is also a big point and it needs a bit more explanation. It is not just the run time, which is affected. It is more a design-fact on how well-written your code will be. Good to know before starting coding are the answers to the following questions:

 

 

Do I have a customizing, which is needed in the beginning?

Does a user use the program more often in a row?

How many users will use this program?

 

With these three questions it is possible to make a decision. If a program is used a lot of times you might read your customizing just once and save it in globals to not need to fetch it again. I think you know what I mean and so I don't want to waste your time here… (You know, all about efficiency in this point)

 

5th point changeability

Is it possible to add additional logic to your coding without having devastating consequences?

Easy point, isn't it? Just make sure, that you implemented your code good and it is possible to change it in an easy way. For example, if you added source in different places and one of it cannot exist without another spot make sure you have a reference.

A very effective way to handle this is to implement unit tests. With a unit test you are just one click away

 

6th point transferability

Is it possible to transfer my code to another spot if needed?

Try to make your source as unattached to the spot as you can. Use all the advantages ABAP (or even your programming language) gives us to develop things. Use interfaces, pass the values and extract your code in own classes / functions as much as you can.

 

7th fact readability

Is my source readable if I would see it for the first time?

  1. If you answer this question with yes, just ask the developer next to you and prove yourself
  2. I think it is an easy point and no need to explain it in a long story.

 

8th point understandability

Will I understand the source in 6 month again?

If you answered the readability question with yes you might say isn't it the same? In my opinion no, because just I can read a source does not mean I understand it. Everybody of us saw a lot of coding and I’m pretty sure most are pretty sure, that it isn't easy to work through a snippet and get the idea behind. You need to see your source in a big picture and here it is needed to understand it. Perhaps you aren't that sure now, so you might add some comment-lines to your source and also describe the methods in a few sentences.

 

9th point learnable

Would I teach someone to code like that?

What? That might be right now in your mind, but this is pretty important. If you see your coding in front of you and you scroll through it, you just should think about all the small details and ask you the question mentioned above. If you think you won’t teach someone developing in this way you might think about it more than twice and change it to something you would teach. That is the fact here.

 

10th point needed

Is my code needed?

Ok, that is not a real question you should ask you after developing your stuff. Might save a lot of work

I just want to make sure, that you are really pretty sure that your developing is needed, if you have any doubts, that might be the implementing is not needed out of any reason ask your questions. I know a lot of developments out there which aren't needed in the end and just wasted time for everybody involved….

 

and ten facts sounds a lot better than nine facts

 

 

 

 

That’s it.

These are my personal  ten recommended points to think about twice. Keep these in your mind I’m pretty sure you will save time, perhaps not during the developing, but afterwards analyzing changing or even enhancing your source.

 

The bridge to the quality management:

Do you think these are points we should consider?

 

Yes?  Here is the fact: The first six are out of the ISO 250XX (Old one ISO9126). Now you might not say again that the quality management is not helping you in your daily work. It is always present but most of us don't recognize it as quality management in a classic way, in my opinion a good thing.

 

A summarization might be:

Just combine beauty and functionality.

 

Feel free to leave a comment and happy coding

 

Cheers

Florian

Hello SCN members,

 

Good Evening.

 

Today i want to explain a simple and very important point particularly about the reports sent to persons like President or Vice-President in a Company.

 

I have been asked to change a report based on the user settings. For your understanding i am giving the screen shot as below:

 

SAP Menu ---> System --->User Profile ----> Own Data, click on the Defaults Tab.

 

defaults screen.png

The above Decimal Notation has 3 types of number formats i.e Space, X and Y.

SPACE" 1.234.567,89

          'X'. " 1,234,567.89

        'Y'. " 1 234 567,89

Different Countries use different Decimal Notations based on their habitat or convenience.


Regarding that i have searched so much and people have used below function module for currency and even quantity fields.

HRCM_STRING_TO_AMOUNT_CONVERT.

But the above function module did not work for all the situations.


So, i have made a change as below:


REPORT  ZTEST_QTY_CONV.

DATA : SS_USR01 TYPE USR01.

DATA: LP_DATA     TYPE REF TO DATA,

         L_THOUSANDS TYPE C LENGTH 1,

         L_DECIMAL   TYPE C LENGTH 1,

         L_TRANSLATE TYPE C LENGTH 2.

DATA: LT_RESULTS TYPE MATCH_RESULT_TAB,

       LS_RESULT  TYPE MATCH_RESULT,

       L_MATCH    TYPE STRING VALUE `^\s*-?\s*(?:\d{1,3}(?:(T?)\d{3})?(?:\1\d{3})*(D\d*)?|D\d+)\s*$`.

DATA: L_INT TYPE STRING,

       L_DEC TYPE STRING,

       INPUT TYPE STRING,

       OUTPUT TYPE STRING.

PARAMETERS : P_QTY TYPE CHAR17.

BREAK-POINT.

FIELD-SYMBOLS: <L_INPUT> TYPE ANY.

CREATE DATA LP_DATA LIKE INPUT.

ASSIGN LP_DATA->* TO <L_INPUT>.

<L_INPUT> = P_QTY.

* Get separator from user record

IF SS_USR01 IS INITIAL.

   SELECT SINGLE * FROM USR01 INTO SS_USR01 WHERE BNAME EQ SY-UNAME.

ENDIF.

CASE SS_USR01-DCPFM.

   WHEN SPACE" 1.234.567,89

     L_THOUSANDS = '.'.

     L_DECIMAL   = ','.

   WHEN 'X'.    " 1,234,567.89

     L_THOUSANDS = ','.

     L_DECIMAL   = '.'.

   WHEN 'Y'.    " 1 234 567,89

     L_THOUSANDS = SPACE.

     L_DECIMAL   = ','.

ENDCASE.

IF SS_USR01-DCPFM <> 'Y'.

* Modify regex to handle the user's selected notation

   REPLACE ALL OCCURRENCES OF 'T' IN L_MATCH WITH L_THOUSANDS.

   else.

     REPLACE ALL OCCURRENCES OF 'T' IN L_MATCH WITH ' '.    " (This statement is not happened)

*so, i did as below,

CLEAR : L_MATCH.

*L_MATCH   = `^\s*-?\s*(?:\d{1,3}(?:(T?)\d{3})?(?:\1\d{3})*(D\d*)?|D\d+)\s*$`.  " Removed the T with space.

L_MATCH   = `^\s*-?\s*(?:\d{1,3}(?:( ?)\d{3})?(?:\1\d{3})*(D\d*)?|D\d+)\s*$`.

ENDIF.

IF L_DECIMAL EQ '.'.

   REPLACE ALL OCCURRENCES OF 'D' IN L_MATCH WITH '\.'.

ELSE.

   REPLACE ALL OCCURRENCES OF 'D' IN L_MATCH WITH L_DECIMAL.

ENDIF.

*  if SS_USR01-DCPFM <> 'Y'.

CONDENSE <L_INPUT> NO-GAPS.

* Check the number is valid

FIND REGEX L_MATCH IN <L_INPUT>.

*  endif.

IF SY-SUBRC IS NOT INITIAL.

   MESSAGE 'Invalid' TYPE 'E'.

*    RAISE EXCEPTION TYPE CX_SY_CONVERSION_NO_NUMBER.

ENDIF.

* Translate thousand separator into "space"

CONCATENATE L_THOUSANDS SPACE INTO L_TRANSLATE.

TRANSLATE <L_INPUT> USING L_TRANSLATE.

* Translate decimal into .

CONCATENATE L_DECIMAL '.' INTO L_TRANSLATE.

TRANSLATE <L_INPUT> USING L_TRANSLATE.

* Remove spaces

CONDENSE <L_INPUT> NO-GAPS.

OUTPUT = <L_INPUT>.

*To get the User profile format after all calculations.

DATA :Quantity  TYPE  STPO-MENGE.

           quantity = output.

write: output, / 'Do calculations and print the values in User Profile Settings:', quantity.


Note: Whenever you have changed the user profile and want to see the result of the values like QUAN (usually 13 digits and 3 decimals), you need log out and log in once. Then only user settings will be applied.


Regards,

Siva kumar. D

Actions

Filter Blog

By author:
By date:
By tag: