[ABAP] Binärdaten (RAW) mit GZIP komprimieren und Base64 codieren

/iwwrk/cl_mgw_workflow_rt_util

TRY.
* Binärdaten
    DATA: lv_xstr TYPE xstring VALUE '54657374737472696E672066C3BC7220646965204B6F6D7072657373696F6E206D697420475A49502E'.
    DATA: lv_xstr_gzip TYPE xstring.

* GZIP komprimieren
    cl_abap_gzip=>compress_binary( EXPORTING raw_in   = lv_xstr
                                   IMPORTING gzip_out = lv_xstr_gzip ).

* Base64 codieren
    DATA(lv_base64_data) = /iwwrk/cl_mgw_workflow_rt_util=>base64_encode( lv_xstr_gzip ).

    WRITE: / lv_base64_data.

  CATCH cx_root INTO DATA(e_txt).
    WRITE: / e_txt->get_text( ).
ENDTRY.

SSFC_BASE64_ENCODE

* Binärdaten
DATA: lv_xstr TYPE xstring VALUE '54657374737472696E672066C3BC7220646965204B6F6D7072657373696F6E206D697420475A49502E'.
DATA: lv_xstr_gzip TYPE xstring.

* GZIP komprimieren
cl_abap_gzip=>compress_binary( EXPORTING raw_in   = lv_xstr
                               IMPORTING gzip_out = lv_xstr_gzip ).

DATA(lv_base64_data) = ||.

* Base64 codieren
CALL FUNCTION 'SSFC_BASE64_ENCODE'
  EXPORTING
    bindata                  = lv_xstr_gzip
  IMPORTING
    b64data                  = lv_base64_data
  EXCEPTIONS
    ssf_krn_error            = 1
    ssf_krn_noop             = 2
    ssf_krn_nomemory         = 3
    ssf_krn_opinv            = 4
    ssf_krn_input_data_error = 5
    ssf_krn_invalid_par      = 6
    ssf_krn_invalid_parlen   = 7
    OTHERS                   = 8.
IF sy-subrc = 0.
  WRITE: / lv_base64_data.
ENDIF.

[ABAP] Base64 codierten String in Binärdaten (RAW) konvertieren und mit GZIP dekomprimieren

/iwwrk/cl_mgw_workflow_rt_util

TRY.
* Base64 String welcher GZIP-komprimierte Binärdaten beinhaltet
    DATA(lv_base64_data) = |C0ktLikuKcrMS1dIO7ynSCElM1XBOz+3oCi1uDgzP08hN7NEwT3KM0APAA==|.

* Base64 decodieren
    DATA(lv_xstr_gzip) = /iwwrk/cl_mgw_workflow_rt_util=>base64_decode( lv_base64_data ).

* Binärdaten
    DATA: lv_xstr TYPE xstring.
    cl_abap_gzip=>decompress_binary( EXPORTING gzip_in = lv_xstr_gzip IMPORTING raw_out = lv_xstr ).

    DATA: lv_str TYPE string.

* xstring (binary) -> string (UTF-8)
    DATA(o_conv) = cl_abap_conv_in_ce=>create( input    = lv_xstr
                                               encoding = 'UTF-8' ).
    o_conv->read( IMPORTING data = lv_str ).

    WRITE: / lv_str.

  CATCH cx_root INTO DATA(e_txt).
    WRITE: / e_txt->get_text( ).
ENDTRY.

SSFC_BASE64_DECODE

* Base64 String welcher GZIP-komprimierte Binärdaten beinhaltet
DATA(lv_base64_data) = |C0ktLikuKcrMS1dIO7ynSCElM1XBOz+3oCi1uDgzP08hN7NEwT3KM0APAA==|.

* Binärdaten
DATA: lv_xstr TYPE xstring.
DATA: lv_xstr_gzip TYPE xstring.

* Base64 decodieren
CALL FUNCTION 'SSFC_BASE64_DECODE'
  EXPORTING
    b64data                  = lv_base64_data
  IMPORTING
    bindata                  = lv_xstr_gzip
  EXCEPTIONS
    ssf_krn_error            = 1
    ssf_krn_noop             = 2
    ssf_krn_nomemory         = 3
    ssf_krn_opinv            = 4
    ssf_krn_input_data_error = 5
    ssf_krn_invalid_par      = 6
    ssf_krn_invalid_parlen   = 7
    OTHERS                   = 8.

IF sy-subrc = 0.
  cl_abap_gzip=>decompress_binary( EXPORTING gzip_in = lv_xstr_gzip IMPORTING raw_out = lv_xstr ).

  DATA: lv_str TYPE string.

* xstring (binary) -> string (UTF-8)
  DATA(o_conv) = cl_abap_conv_in_ce=>create( input    = lv_xstr
                                             encoding = 'UTF-8' ).
  o_conv->read( IMPORTING data = lv_str ).

  WRITE: / lv_str.
ENDIF.

[ABAP] Spoolaufträge (OTF, OTFHPL2, SMART, LIST, TEXT) in PDF wandeln

Variante 1 (cl_rspo_spoolid_to_pdf)

* Nr. des Spoolauftrags
PARAMETERS: p_spool TYPE rspoid OBLIGATORY.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_spool.

  SELECT rqident, rqtitle, rqdest, rqpaper, rqdoctype
    INTO TABLE @DATA(it_spool)
    FROM tsp01
    ORDER BY rqident.

  DATA: it_return TYPE TABLE OF ddshretval.

* eigene Suuchhilfe für Auswahl der Spooljobs anzeigen
  CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST'
    EXPORTING
      retfield        = 'RQIDENT'     " Spalte der internen Tabelle bei value_tab
      dynpprog        = sy-repid
      dynpnr          = sy-dynnr
      dynprofield     = 'P_SPOOL'     " Name des Dynpro-Feldes für die automatische Werterückgabe
      value_org       = 'S'           " Werteübergabe: C: zellenweise, S: strukturiert
      window_title    = 'Auswahl'
    TABLES
      value_tab       = it_spool      " Übergabe-Tabelle mit Werten für die Anzeige und Auswahl
      return_tab      = it_return     " Rückgabe-Tabelle mit den ausgewählten (geklickten) Elementen
    EXCEPTIONS
      parameter_error = 1
      no_values_found = 2
      OTHERS          = 3.

  IF sy-subrc = 0.
    IF lines( it_return ) > 0.
      MESSAGE it_return[ 1 ]-fieldval TYPE 'S'.
    ENDIF.
  ENDIF.

START-OF-SELECTION.
  TRY.
      DATA: lv_xstring_pdf TYPE xstring.
      DATA: lv_size TYPE int4.
      DATA: lv_jobname TYPE btcjob.
      DATA: lv_jobcount TYPE btcjobcnt.

* SPOOL-ID -> PDF
      cl_rspo_spoolid_to_pdf=>get_spool_pdf( EXPORTING
                                               iv_rqident       = p_spool
                                               iv_no_background = abap_true
                                             IMPORTING
                                               ev_pdf           = lv_xstring_pdf
                                               ev_size          = lv_size
                                               ev_jobname       = lv_jobname
                                               ev_jobcount      = lv_jobcount
                                           ).

      WRITE: / 'Size:', lv_size.
      WRITE: / 'Jobname:', lv_jobname.
      WRITE: / 'Jobcount', lv_jobcount.

      DATA: lv_action TYPE i.
      DATA: lv_filename TYPE string.
      DATA: lv_fullpath TYPE string.
      DATA: lv_path TYPE string.

* SaveDialog
      cl_gui_frontend_services=>file_save_dialog( EXPORTING
                                                    default_file_name = 'test.pdf'
                                                    default_extension = 'pdf'
                                                    file_filter       = '(*.pdf)|*.pdf|'
                                                  CHANGING
                                                    filename          = lv_filename
                                                    path              = lv_path
                                                    fullpath          = lv_fullpath
                                                    user_action       = lv_action ).

      IF lv_action EQ cl_gui_frontend_services=>action_ok.

* xstring -> solix
        DATA(it_raw_data) = cl_bcs_convert=>xstring_to_solix( lv_xstring_pdf ).

* PDF lokal speichern
        cl_gui_frontend_services=>gui_download( EXPORTING
                                                  filename     = lv_fullpath
                                                  filetype     = 'BIN'
                                                  bin_filesize = lv_size
                                                CHANGING
                                                  data_tab     = it_raw_data ).

* Browserfenster erzeugen
        DATA(o_html) = NEW cl_gui_html_viewer( parent = cl_gui_container=>default_screen ).

* PDF im ABAP-Fenster anzeigen
        o_html->show_url( url      = CONV skwf_url( lv_fullpath )
                          in_place = abap_true ).

* leere SAP-Toolbar ausblenden
        cl_abap_list_layout=>suppress_toolbar( ).

* cl_gui_container=>default_screen erzwingen
        WRITE: / |Datei erfolgreich unter { lv_fullpath } gespeichert.|.
      ENDIF.
    CATCH cx_root INTO DATA(e_txt).
  ENDTRY.

Variante 2 (CONVERT_OTFSPOOLJOB_2_PDF, CONVERT_ABAPSPOOLJOB_2_PDF)

* Nr. des Spoolauftrags
PARAMETERS: p_spool TYPE rspoid OBLIGATORY.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_spool.

  DATA: it_return TYPE TABLE OF ddshretval.

  SELECT rqident, rqtitle, rqdest, rqpaper, rqdoctype
    INTO TABLE @DATA(it_spool)
    FROM tsp01
    ORDER BY rqident.

* eigene Suuchhilfe für Auswahl der Spooljobs anzeigen
  CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST'
    EXPORTING
      retfield        = 'RQIDENT'     " Spalte der internen Tabelle bei value_tab
      dynpprog        = sy-repid
      dynpnr          = sy-dynnr
      dynprofield     = 'P_SPOOL'     " Name des Dynpro-Feldes für die automatische Werterückgabe
      value_org       = 'S'           " Werteübergabe: C: zellenweise, S: strukturiert
      window_title    = 'Auswahl'
    TABLES
      value_tab       = it_spool      " Übergabe-Tabelle mit Werten für die Anzeige und Auswahl
      return_tab      = it_return     " Rückgabe-Tabelle mit den ausgewählten (geklickten) Elementen
    EXCEPTIONS
      parameter_error = 1
      no_values_found = 2
      OTHERS          = 3.

  IF sy-subrc = 0.
    IF lines( it_return ) > 0.
      MESSAGE it_return[ 1 ]-fieldval TYPE 'S'.
    ENDIF.
  ENDIF.

START-OF-SELECTION.

  SELECT SINGLE rqclient, rqo1name
    INTO @DATA(lv_tsp01_sel)
    FROM tsp01
    WHERE rqident = @p_spool.

  IF sy-subrc = 0.
* Dummys für Aufruf von RSPO_RETURN_SPOOLJOB
    DATA: it_otf TYPE STANDARD TABLE OF soli.
    DATA: it_pdf TYPE STANDARD TABLE OF tline.

* Beispiel: Spooljob als iTabs holen
    CALL FUNCTION 'RSPO_RETURN_SPOOLJOB'
      EXPORTING
        rqident              = p_spool
        desired_type         = 'RAW' " RAW, ALI, OTF, PDF
      TABLES
        buffer               = it_otf
        buffer_pdf           = it_pdf
      EXCEPTIONS
        no_such_job          = 1
        job_contains_no_data = 2
        selection_empty      = 3
        no_permission        = 4
        can_not_access       = 5
        read_error           = 6
        type_no_match        = 7
        OTHERS               = 8.

    IF sy-subrc <> 0.
      WRITE: 'Spoolauftrag nicht vorhanden: ', sy-subrc.
    ELSE.
      SELECT SINGLE rqdoctype FROM tsp01 INTO @DATA(lv_objtype) WHERE rqident = @p_spool.

      IF sy-subrc <> 0.
        WRITE: 'Fehler beim Lesen des Doctypes.', sy-subrc.
      ELSE.
        DATA: lv_numbytes TYPE i.
        DATA: lv_pdfspoolid LIKE tsp01-rqident.
        DATA: lv_jobname TYPE tbtcjob-jobname.
        DATA: lv_jobcount TYPE tbtcjob-jobcount.
        DATA: lv_xstring_pdf TYPE xstring.

* Konvertierung anhand des Typs vornehmen
* OTF, SMART
        CASE lv_objtype.
          WHEN 'OTF' OR 'OTFHPL2' OR 'SMART'.
            CALL FUNCTION 'CONVERT_OTFSPOOLJOB_2_PDF'
              EXPORTING
                src_spoolid              = p_spool
                no_dialog                = abap_true
                pdf_destination          = 'X' " xstring
                no_background            = abap_false
              IMPORTING
                pdf_bytecount            = lv_numbytes
                pdf_spoolid              = lv_pdfspoolid
                btc_jobname              = lv_jobname
                btc_jobcount             = lv_jobcount
                bin_file                 = lv_xstring_pdf
              EXCEPTIONS
                err_no_otf_spooljob      = 1
                err_no_spooljob          = 2
                err_no_permission        = 3
                err_conv_not_possible    = 4
                err_bad_dstdevice        = 5
                user_cancelled           = 6
                err_spoolerror           = 7
                err_temseerror           = 8
                err_btcjob_open_failed   = 9
                err_btcjob_submit_failed = 10
                err_btcjob_close_failed  = 11.

            IF sy-subrc <> 0.
              WRITE: / 'Fehler bei der OTF->PDF Konvertierung:', sy-subrc.
            ELSE.

            ENDIF.
* ALI
          WHEN 'LIST' OR 'TEXT'.
            CALL FUNCTION 'CONVERT_ABAPSPOOLJOB_2_PDF'
              EXPORTING
                src_spoolid              = p_spool
                no_dialog                = abap_true
                pdf_destination          = 'X' " xstring
                no_background            = abap_false
              IMPORTING
                pdf_bytecount            = lv_numbytes
                pdf_spoolid              = lv_pdfspoolid
                btc_jobname              = lv_jobname
                btc_jobcount             = lv_jobcount
                bin_file                 = lv_xstring_pdf
              EXCEPTIONS
                err_no_abap_spooljob     = 1
                err_no_spooljob          = 2
                err_no_permission        = 3
                err_conv_not_possible    = 4
                err_bad_destdevice       = 5
                user_cancelled           = 6
                err_spoolerror           = 7
                err_temseerror           = 8
                err_btcjob_open_failed   = 9
                err_btcjob_submit_failed = 10
                err_btcjob_close_failed  = 11.

            IF sy-subrc <> 0.
              WRITE: / 'Fehler bei der TEXT->PDF Konvertierung: ', sy-subrc.
            ELSE.

            ENDIF.

          WHEN 'ADSP'.
            WRITE: / 'Adobe Forms derzeit nicht implementiert.'.
          WHEN OTHERS.
            WRITE: / 'Verarbeitungsschritt derzeit nicht implementiert:', lv_objtype.
        ENDCASE.

* wenn erfolgreich -> PDF speichern
        IF lv_numbytes > 0 AND xstrlen( lv_xstring_pdf ) > 0.
          DATA: lv_action TYPE i.
          DATA: lv_filename TYPE string.
          DATA: lv_fullpath TYPE string.
          DATA: lv_path TYPE string.

          TRY.
* SaveDialog
              cl_gui_frontend_services=>file_save_dialog( EXPORTING
                                                            default_file_name = |{ p_spool }|
                                                            default_extension = 'pdf'
                                                            file_filter       = '(*.pdf)|*.pdf|'
                                                          CHANGING
                                                            filename          = lv_filename
                                                            path              = lv_path
                                                            fullpath          = lv_fullpath
                                                            user_action       = lv_action ).

              IF lv_action EQ cl_gui_frontend_services=>action_ok.
* xstring -> solix
                DATA(it_raw_data) = cl_bcs_convert=>xstring_to_solix( lv_xstring_pdf ).

* PDF lokal speichern
                cl_gui_frontend_services=>gui_download( EXPORTING
                                                          filename     = lv_fullpath
                                                          filetype     = 'BIN'
                                                          bin_filesize = lv_numbytes
                                                        CHANGING
                                                          data_tab     = it_raw_data ).

* Browserfenster erzeugen
                DATA(o_html) = NEW cl_gui_html_viewer( parent = cl_gui_container=>default_screen ).

* PDF im ABAP-Fenster anzeigen
                o_html->show_url( url      = CONV skwf_url( lv_fullpath )
                                  in_place = abap_true ).

* leere SAP-Toolbar ausblenden
                cl_abap_list_layout=>suppress_toolbar( ).

* cl_gui_container=>default_screen erzwingen
                WRITE: / |Datei erfolgreich unter { lv_fullpath } gespeichert.|.
              ENDIF.

            CATCH cx_root INTO DATA(e_text).          " Oberklasse für Exceptions abfangen und Kurztext übergeben
              WRITE: / e_text->get_text( ).
          ENDTRY.

        ENDIF.
      ENDIF.
    ENDIF.
  ELSE.
    WRITE: / |Spoolauftrag { p_spool } nicht vorhanden.|.
  ENDIF.

[ABAP] string-Formatierungen mit String-Templates

* ab V7.31
* http://zevolving.com/2013/07/abap-string-templates-new-feature-in-abap-731/

* Hinweis: String-Templates funktionieren nur Programmen bei denen die Unicodeprüfung aktiv ist!
* Z.B. kommt es in Modulpool-Programmen zu Fehlern beim kompilieren des Codes und der Compiler moniert die '|'-Symbole

DATA: text TYPE string VALUE 'Text'.
DATA: atext TYPE string VALUE '012345'.
DATA: num TYPE p DECIMALS 3 VALUE '-123.45'.

* Case
WRITE: / |RAW:   { text CASE = RAW }|.   " Text
WRITE: / |LOWER: { text CASE = LOWER }|. " text
WRITE: / |UPPER: { text CASE = UPPER }|. " TEXT

* Align
WRITE: / |LEFT:   ->{ text WIDTH = 20 ALIGN = LEFT }<--|.   " >Text                <
WRITE: / |RIGHT:  ->{ text WIDTH = 20 ALIGN = RIGHT }<--|.  " >                Text<
WRITE: / |CENTER: ->{ text WIDTH = 20 ALIGN = CENTER }<--|. " >        Text        <

* Padding, Auffüllen mit definiertem Zeichen
WRITE: / |PAD: { text ALIGN = LEFT WIDTH = 20 PAD = '_' }|.   " Text________________
WRITE: / |PAD: { text ALIGN = RIGHT WIDTH = 20 PAD = '_' }|.  " ________________Text
WRITE: / |PAD: { text ALIGN = CENTER WIDTH = 20 PAD = '_' }|. " ________Text________

* führende Nullen hinzufügen, ersetzt FUBA 'CONVERSION_EXIT_ALPHA_INPUT'
WRITE: / |ALPHA: { atext WIDTH = 20 ALPHA = IN }|. " 00000000000000012345
* führende Nullen entfernen, ersetzt FUBA 'CONVERSION_EXIT_ALPHA_OUTPUT'
WRITE: / |ALPHA: { atext ALPHA = OUT }|.           " 12345

* Zahlen 1
WRITE: / |RAW:         { num NUMBER = RAW }|.         " -123.45
WRITE: / |USER:        { num NUMBER = USER }|.        " -123,45
WRITE: / |ENVIRONMENT: { num NUMBER = ENVIRONMENT }|. " -123,45

* Zahlen 2
WRITE: / |SCIENTIFIC:                   { num STYLE = SCIENTIFIC }|.                   " -1.2345E+02
WRITE: / |SCIENTIFIC_WITH_LEADING_ZERO: { num STYLE = SCIENTIFIC_WITH_LEADING_ZERO }|. " -0.12345E+03
WRITE: / |SCALE_PRESERVING_SCIENTIFIC:  { num STYLE = SCALE_PRESERVING_SCIENTIFIC }|.  " -1.2345E+0002
WRITE: / |ENGINEERING:                  { num STYLE = ENGINEERING }|.                  " -123.45E+00

* Datumsformat
WRITE: / |RAW:         { sy-datum DATE = RAW }|.         " 20160705
WRITE: / |ISO:         { sy-datum DATE = ISO }|.         " 2016-07-05
WRITE: / |USER:        { sy-datum DATE = USER }|.        " 05.07.2016
WRITE: / |ENVIRONMENT: { sy-datum DATE = ENVIRONMENT }|. " 05.07.2016

* Uhrzeitformat
WRITE: / |RAW:         { sy-uzeit TIME = RAW }|.         " 102336
WRITE: / |ISO:         { sy-uzeit TIME = ISO }|.         " 10:23:36
WRITE: / |USER:        { sy-uzeit TIME = USER }|.        " 10:23:36
WRITE: / |ENVIRONMENT: { sy-uzeit TIME = ENVIRONMENT }|. " 10:23:36

* Timestampformat
DATA: lv_tsl TYPE timestampl.
* Zeitstempel lang
GET TIME STAMP FIELD lv_tsl.
WRITE: / |SPACE:       { lv_tsl TIMESTAMP = SPACE }|.       " 2016-07-05 08:23:36.0908600
WRITE: / |ISO:         { lv_tsl TIMESTAMP = ISO }|.         " 2016-07-05T08:23:36,0908600
WRITE: / |USER:        { lv_tsl TIMESTAMP = USER }|.        " 05.07.2016 08:23:36,0908600
WRITE: / |ENVIRONMENT: { lv_tsl TIMESTAMP = ENVIRONMENT }|. " 05.07.2016 08:23:36,0908600

* ABAP Serialization XML (axXML)
WRITE: / |XML: { lv_tsl XSD = YES }|. " 20160705082336.09086

[ABAP] Email mit PDF-Anhang versenden

* https://www.sapnuts.com/tutorials/Sending-email-with-attachment-in-SAP-ABAP/singlepage.html
* http://www.sapwiki.cl/wiki/index.php?title=SAP_ABAP_CORREO
* http://www.berater-wiki.de/index.php?title=PDF_per_Mail/Fax_verschicken
* https://wiki.scn.sap.com/wiki/display/ABAP/Sending+Mails+-+Home+Page?original_fqdn=wiki.sdn.sap.com#SendingMails-HomePage-CL_BCS
* http://www.apentia-forum.de/viewtopic.php?f=2&t=74925&start=0

PARAMETERS: p_email TYPE adr6-smtp_addr OBLIGATORY DEFAULT 'email@email.com'.     " Email-Adr. des Empfängers
PARAMETERS: p_subj TYPE string DEFAULT 'Testmail.'.                               " Email-Subject
PARAMETERS: p_bdytxt TYPE soli DEFAULT 'Testtext.'.                               " eine Mailtext-Zeile
PARAMETERS: p_pdf TYPE file_table-filename OBLIGATORY DEFAULT 'C:\Temp\Test.pdf'. " PDF-Anhang

* wenn die F4-Hilfe für den Dateinamen aufgerufen wird
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_pdf.
  DATA: lv_rc TYPE i.
  DATA: it_files TYPE filetable.
  DATA: lv_action TYPE i.

* File-Tabelle leeren, da hier noch alte Einträge von vorherigen
* Aufrufen drin stehen können
  CLEAR: it_files.
* FileOpen-Dialog aufrufen
  TRY.
      cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                    file_filter = |PDF (*.pdf)\|*.pdf\|{ cl_gui_frontend_services=>filetype_all }|
                                                  CHANGING
                                                    file_table  = it_files     " ausgewählte Dateien
                                                    rc          = lv_rc        " Anzahl ausgewählte Dateien, -1 bei Fehler
                                                    user_action = lv_action ).

      IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn Datei ausgewählt wurde
        IF lines( it_files ) > 0.
* ersten Tabelleneintrag lesen
          p_pdf = it_files[ 1 ]-filename.
        ENDIF.
      ENDIF.
    CATCH cx_root INTO DATA(e_text).
      MESSAGE e_text->get_text( ) TYPE 'S'.
  ENDTRY.

START-OF-SELECTION.
  DATA: lv_pdf_size TYPE i.
  DATA: it_pdf_data_tab TYPE solix_tab.

  TRY.
* PDF-Datei vom lokalen Rechner auf den Appl.-Server hochladen
      cl_gui_frontend_services=>gui_upload( EXPORTING
                                              filename   = |{ p_pdf }|
                                              filetype   = 'BIN'
                                            IMPORTING
                                              filelength = lv_pdf_size
                                            CHANGING
                                              data_tab   = it_pdf_data_tab ).

      IF sy-subrc = 0.
* it_body_txt beinhaltet die Zeilen des Body-Textes
* die Zeilen sind jeweils char255 Zeichen lang
* eine Zeile (line) ist vom Typ SOLI
        DATA(it_body_txt) = VALUE soli_tab( ( line = p_bdytxt ) ).

* Document (Mail) erzeugen
* Email-Subject (i_subject) darf hier max. 50 Zeichen lang sein (Subject-Darstellung in der SOST)
* die "richtige" Übergabe des Email-Subjects erfolgt weiter unten in der Funktion set_message_subject( )
        DATA(o_document) = cl_document_bcs=>create_document( i_type    = 'RAW'
                                                             i_text    = it_body_txt
                                                             i_subject = CONV so_obj_des( p_subj ) ).

* Attachement hinzufügen -> Typen siehe Tabelle TSOTD
* siehe auch: http://www.apentia-forum.de/viewtopic.php?f=2&t=74925&start=0
        o_document->add_attachment( i_attachment_type    = 'EXT'
                                    i_attachment_subject = |{ p_pdf }|
                                    i_att_content_hex    = it_pdf_data_tab ).

* Sendrequest erzeugen
        DATA(o_send_request) = cl_bcs=>create_persistent( ).
* Email-Subject festlegen, ip_subject ist vom Typ String
        o_send_request->set_message_subject( ip_subject = p_subj ).
* Die Mail an den Sendrequest hängen
        o_send_request->set_document( o_document ).

* Sender
* SAP-User als Sender
        DATA(o_sender) = cl_sapuser_bcs=>create( sy-uname ).
* Email-Adresse als Sender
*        DATA(o_sender) = cl_cam_address_bcs=>create_internet_address( i_address_string = 'email@email.com' ).
        o_send_request->set_sender( o_sender ).

* Empfänger -> beachte: u.U. sind Email-Adressen mit Umlauten nicht zulässig -> Exception
        DATA(o_recipient) = cl_cam_address_bcs=>create_internet_address( p_email ).
        o_send_request->add_recipient( i_recipient = o_recipient
                                       i_express = abap_true
                                     " i_copy      = abap_true " CC
                                     " i_blind_copy = abap_true " BCC
                                     ).

* Sofort senden
        o_send_request->set_send_immediately( abap_true ).

* Dokument senden
        IF o_send_request->send( i_with_error_screen = abap_true ) = abap_true.
          WRITE: / 'Email gesendet.'.
        ELSE.
          WRITE: / 'Fehler beim Email-Versand.'.
        ENDIF.

        DATA: lv_in_update_task TYPE sy-subrc.

* Überprüft, ob das Programm im Verbucher abläuft
        CALL FUNCTION 'TH_IN_UPDATE_TASK'
          IMPORTING
            in_update_task = lv_in_update_task.

        IF lv_in_update_task EQ 0.
* Persistenzdienste des Objektes starten, sonst wird ggf. die Mail nicht gesendet
          COMMIT WORK.
        ENDIF.

      ENDIF.
    CATCH cx_root INTO DATA(e_text).
      WRITE: / e_text->get_text( ).
  ENDTRY.