*---------------------------------------------------------------------*
*                    Z _ B J H _ E X C E L _ D O I                    *
*                                                                     *
*                  Autor: Bernd Haase (BJH Software)                  *
*---------------------------------------------------------------------*
* SAP R/3 DOI - Desktop Office Integration. Excel Mappe am Bildschirm *
* Beispielprogramm zum Aufblenden einer Excel-Mappe. In das Excel kann*
* der Tabelleninhalt einer beliebigen SAP Tabelle importiert werden.  *
* Nach dortiger Bearbeitung werden die Daten wieder nach SAP expor-   *
* tiert und stehen dort für die weitere Bearbeitung bereit.           *
*---------------------------------------------------------------------*
* Hinweis beim Vergleich: um keine Includes in diesem Programm zu ver-*
* wenden, ist der vergleich auf numerische Zahlen und Datumsfelder    *
* immer negativ, wegen der unterschiedlichen Aufbereitungsweise in SAP*
* und Excel                                                           *
*---------------------------------------------------------------------*
* (DX) DOI, Excel OO                                                  *
*---------------------------------------------------------------------*
* 06.06.2005 BHaa: Programm erstellt aus Vorlage                      *
*---------------------------------------------------------------------*
REPORT z_bjh_excel_doi MESSAGE-ID sap_doi.

TYPE-POOLS: soi.

CONSTANTS:
* Bereichsnamen für die Excel-Mappe festlegen, Formatzuordnung
 co_format_char(04)      TYPE c VALUE 'CHAR',
 co_format_num(04)       TYPE c VALUE 'NUM ',
 co_format_dats(04)      TYPE c VALUE 'DATS',
 co_format_titel(05)     TYPE c VALUE 'TITEL',
 co_format_kommentar(05) TYPE c VALUE 'NOTIZ'.

DATA:
* SAP DOI (Desktop Office Integration) Interfaces
 c_container      TYPE REF TO cl_gui_custom_container,
 c_kontrolle      TYPE REF TO i_oi_container_control,
 document         TYPE REF TO i_oi_document_proxy,
 spreadsheet      TYPE REF TO i_oi_spreadsheet,
 error            TYPE REF TO i_oi_error,
 errors           TYPE REF TO i_oi_error OCCURS 0 WITH HEADER LINE.

DATA:
* Variablen speziell zur Klasse document
 retcode          TYPE soi_ret_string,
 oo_interface_verfuegbar TYPE i.

****************************
* Formatierung Excel-Mappe *
****************************
DATA:
 wa_bereich       TYPE soi_range_item,
 itab_bereich     TYPE soi_range_list,

 wa_def_bereich   TYPE soi_full_range_item,
 itab_def_bereich TYPE soi_full_range_table.

***************
* Dynpro 0100 *
***************
DATA:
 erster_lauf(1) TYPE c VALUE ' ',
 okcode LIKE sy-ucomm.

***************
* Excel - SAP *
***************
DATA:
 wa_excel_out     TYPE soi_generic_item,
 itab_excel_out   TYPE soi_generic_table,
 z_excel_reihe(4) TYPE n,
 z_excel_spalte   LIKE z_excel_reihe,

 zw_excel         TYPE soi_generic_item,
 zw_out_row       TYPE soi_generic_item-row,
* Eingabestruktur und Ausgabestruktur der Daten gleich
 wa_in_out_gleich(1) TYPE c.

FIELD-SYMBOLS:
 <fs_excel> LIKE wa_excel_out.

******************
* Eingabetabelle *
******************
TYPES:
 max_ausrichtung(500) TYPE c,
 max_schluessel(100)  TYPE c.

DATA:
 wa_input      TYPE max_ausrichtung,
 wa_output     TYPE max_ausrichtung,
 itab_input    TYPE TABLE OF max_ausrichtung,
 itab_output   TYPE TABLE OF max_ausrichtung,
 wa_in_schl    TYPE max_schluessel,
 BEGIN OF wa_out_schl,
   idx LIKE sy-tabix,          "Referenz auf Datensatz itab_output
   gef(1) TYPE c,              "Abgleich beim Vergleich. Satz gefunden
   t TYPE max_schluessel,      "Schlüsselbegriff
 END OF wa_out_schl,
 itab_out_schl LIKE HASHED TABLE OF wa_out_schl WITH UNIQUE KEY t,

* Referenztabelle bei Vergleichsform "ohne Protokoll". Es wird nur
* protokolliert, welche Einträge aus der Eingabetabelle einem Schlüssel
* der Ausgabetabelle entsprachen. Bei nachfolgenden Abprüfen der
* Eingabetabelle auf nicht zugeordnete Ausgabetabellenschlüssel braucht
* nur über den Positionzeiger der Eingangstabelle geprüft werden
 BEGIN OF wa_ohne_schl,
   idx LIKE sy-tabix,
 END OF wa_ohne_schl,
 itab_ohne_schl LIKE HASHED TABLE OF wa_ohne_schl WITH UNIQUE KEY idx,

* Vergleich gegen die Struktur, bei Bedarf anpassen
 anzahl_i           TYPE i,
 laenge_input       LIKE anzahl_i,
 laenge_out_schl    LIKE anzahl_i,
 anzahl_zeilen_in   LIKE anzahl_i,
 anzahl_spalten_in  LIKE anzahl_i,
 anzahl_zeilen_out  LIKE anzahl_i,
 anzahl_spalten_out LIKE anzahl_i.

DATA:
* Felder und Struktur der eingegebenen Tabelle
 BEGIN OF wk_dd03l,
   fieldname LIKE dd03l-fieldname,
   position  LIKE dd03l-position,
   keyflag   LIKE dd03l-keyflag,
   rollname  LIKE dd03l-rollname,
   intlen    LIKE dd03l-intlen,
   inttype   LIKE dd03l-inttype,
   ddtext    LIKE dd04t-ddtext,
* ab welcher Position (Spalte) beginnt das Feld, Anfang =0
   offset    TYPE ioff,
 END OF wk_dd03l,
 wktab_dd03l LIKE TABLE OF wk_dd03l,

* Schlüssel, beginnt immer ab Position 0, hat aber wieviele Stellen
 wa_schl_offset TYPE ioff.

FIELD-SYMBOLS:
 <fs_dd03l> LIKE wk_dd03l.

*********************
* Daten importieren *
*********************
DATA:
 dref TYPE REF TO data,

 tabname LIKE x030l-tabname,
* Länge der Struktur, wird zum Vergleich benötigt, ob die Struktur noch
* in die Inputtabelle zu übertragen ist
 laenge_tabelle TYPE i,

* wieviele Datensätze einlesen
 anzahl_saetze(3) TYPE n.

FIELD-SYMBOLS:
 <fs_tabelle> TYPE ANY.

****************
* Markierungen *
****************
* beim Rücksprung aus der Excel-Mappe feststellen, welche Bereiche
* markiert worden sind
DATA:
* Einzelbereich markiert
 wa_markiert    TYPE soi_area_item,
* Bereiche markiert
 itab_markiert  TYPE soi_area_table,
 itab_rowsid    TYPE soi_map_table,
 itab_columnsid TYPE soi_map_table.

***********************************************************************
*                                                                     *
*                     P R O G R A M M S T A R T                       *
*                                                                     *
***********************************************************************
* durchstarten mit Bildschirm 100
CALL SCREEN 100.

AT USER-COMMAND.
 CASE sy-ucomm.
   WHEN 'DYN'.
* um wieder aus der Reporting-Statistik herauszukommen.
     LEAVE LIST-PROCESSING.
 ENDCASE.

*---------------------------------------------------------------------*
*                    D Y N P R O _ 0 1 0 0 _ P B O                    *
*---------------------------------------------------------------------*
* Container Dynpro für die Excel-Mappe generieren beim ersten Durch-  *
* lauf. Die Daten werden erst auf Eingabe aufgebaut, da dort die      *
* Tabelle eingegeben wird.                                            *
*---------------------------------------------------------------------*
MODULE status_0100 OUTPUT.

* Funktionstasten und Titelzeile
* bei den Funktionstasten muß mindestens eine mit dem Funktionscode
* 'DYN' vorhanden sein zum Rücksprung auf das Dynpro
 SET PF-STATUS '100'.
*  SET TITLEBAR '100'.

* wurde bereits die Basisumgebungung aufgebaut, wenn nicht, dieses
* jetzt durchführen
 CHECK erster_lauf IS INITIAL.

 MOVE 'N' TO erster_lauf.

* die SAP DOI Schnittstellenreferenzen holen
* als erstes die SAP DOI i_oi_container_control Schnittstelle
 CALL METHOD c_oi_container_control_creator=>get_container_control
      IMPORTING
           control = c_kontrolle
           error   = error.
* prüfen, ob ein Fehler aufgetreten ist oder erfolgreich
 CALL METHOD error->raise_message
      EXPORTING
           type = 'E'.

* den Container im Bildschirm generieren. Der Containername muß genauso
* heißen wie das Custom Control im Dynpro !!!
 CREATE OBJECT c_container
        EXPORTING
             container_name = 'CONTAINER'.

* den SAP DOI Container initialisieren. Dem den eine Anweisung zuvor
* aufgebauten Objekt zuordnen und referenzieren
 CALL METHOD c_kontrolle->init_control
      EXPORTING
           r3_application_name      = 'R/3 Basis'
           inplace_enabled          = 'X'
           inplace_scroll_documents = 'X'
           parent                   = c_container
           register_on_close_event  = 'X'
           register_on_custom_event = 'X'
           no_flush                 = 'X'
      IMPORTING
           error                    = errors.
* Fehlerprotokolltabelle erweitern
 APPEND errors.

* dem SAP DOI container sagen, daß eine Excel-Mappe eingespeist werden
* soll
 CALL METHOD c_kontrolle->get_document_proxy
      EXPORTING
           document_type  = 'Excel.Sheet'
           no_flush       = 'X'
*           REGISTER_CONTAINER = 'X'
      IMPORTING
           document_proxy = document
           error          = errors.
* Fehlerprotokolltabelle erweitern
 APPEND errors.

* und nun das Dokument (die Excel-Mappe) generieren
 CALL METHOD document->create_document
      EXPORTING
           open_inplace   = 'X'
           document_title = 'R/3 Tabelle in Excel darstellen'
           no_flush       = 'X'
      IMPORTING
           error          = errors.
* Fehlerprotokolltabelle erweitern
 APPEND errors.

* prüfen, ob das Dokument (Excel-Mappe) erfolgreich angelegt werden
* konnte und auf diese dann mit der Klasse 'spreadsheet' referenzieren
 CALL METHOD document->has_spreadsheet_interface
      EXPORTING
           no_flush     = 'X'
      IMPORTING
           is_available = oo_interface_verfuegbar
           error        = errors.
 APPEND errors.

 CALL METHOD document->get_spreadsheet_interface
      EXPORTING
           no_flush        = ' '
      IMPORTING
           sheet_interface = spreadsheet
           error           = errors.
 APPEND errors.

* Schleife über alle gefundenen Fehler, da Get_spreadsheet_interface
* flushed und synchronisiert the automation queue !
 LOOP AT errors.
   CALL METHOD errors->raise_message
        EXPORTING
             type = 'E'.
 ENDLOOP.
 REFRESH: errors. FREE errors.

ENDMODULE.

*---------------------------------------------------------------------*
*                   D Y N P R O _ 0 1 0 0 _ E N D E                   *
*---------------------------------------------------------------------*
* Programm beenden und die Klassenobjekte löschen und freigeben.      *
*---------------------------------------------------------------------*
MODULE upro-dynpro_0100_ende INPUT.

 CASE okcode.
   WHEN 'STOP'.
     IF NOT document IS INITIAL.
       CALL METHOD document->close_document.
       FREE document.
     ENDIF.
     IF NOT c_kontrolle IS INITIAL.
       CALL METHOD c_kontrolle->destroy_control.
       FREE c_kontrolle.
     ENDIF.

     LEAVE PROGRAM.
 ENDCASE.
ENDMODULE.

*---------------------------------------------------------------------*
*                        D Y N P R O _ 0 1 0 0                        *
*---------------------------------------------------------------------*
* auf die Druckknöpfe im Bildschirm (auch die der Gui-Status reagieren*
*---------------------------------------------------------------------*
MODULE user_command_0100 INPUT.

 CASE okcode.
**********
* Import *
**********
   WHEN 'IMP'.
* Aufbauen der Objekte und die ausgewählte Tabelle einlesen
     PERFORM upro-daten_import.
* Überschriftszeile
     PERFORM upro-daten_import_titel.

* Daten in die am Bildschirm aufgebaute Excel-Maske importieren, auf
* die Excel-Tabelle umschiften und anzeigen.
     PERFORM upro-daten_import_bs.

**********
* Export *
**********
   WHEN 'EXP'.
* Daten aus dem Excel exportieren. Die bereit gestellte Tabelle dient
* dann zur weiteren Verarbeitung
     PERFORM upro-daten_export.

     IF anzahl_spalten_in <> anzahl_spalten_out.
       MESSAGE i000(zc) WITH 'Spaltenanzahl unterschiedlich'.
     ENDIF.

*************
* Vergleich *
*************
   WHEN 'PRT'.
* Protokoll ausgeben, ganz billig als Reporting Liste. Ausrichtungsform
* beim Vergleich beachten
     PERFORM upro-protokoll_ohne.
*     PERFORM upro-protokoll_mit.

**************
* Markierung *
**************
   WHEN 'MARK'.
* markierte Bereiche einlesen
* für den einfacheren Zugriff (der erst markierte Bereich gewinnt)
     CALL METHOD spreadsheet->get_selection
          EXPORTING
               no_flush = ' '
          IMPORTING
               top      = wa_markiert-top
               left     = wa_markiert-left
               rows     = wa_markiert-rows
               columns  = wa_markiert-columns
               error    = errors
               retcode  = retcode.

* alle Bereiche, die markiert worden sind, auswerten. Interessant sind
* die Wert von itab_markiert.
     CALL METHOD spreadsheet->get_selected_areas
          EXPORTING
               no_flush = ' '
          IMPORTING
               areas     = itab_markiert
               rowsid    = itab_rowsid
               columnsid = itab_columnsid
               error     = errors
               retcode   = retcode.

**************
* Rücksprung *
**************
   WHEN 'BACK'.
     LEAVE TO SCREEN 0.

****************
* Programmende *
****************
* bitte auch Modul Direktabbruch beachten
   WHEN 'STOP'.
     IF NOT document IS INITIAL.
       CALL METHOD document->close_document.
       FREE document.
     ENDIF.
     IF NOT c_kontrolle IS INITIAL.
       CALL METHOD c_kontrolle->destroy_control.
       FREE c_kontrolle.
     ENDIF.

     LEAVE PROGRAM.
 ENDCASE.

 CLEAR okcode.

ENDMODULE.

*---------------------------------------------------------------------*
*                   D A T E N _ I M P O R T I E R E N                 *
*---------------------------------------------------------------------*
* aus Excel-Sicht: Daten werden aus dem SAP in Excel importiert. Diese*
* Section übernimmt nur die allgemeinen Definitionen für die Excel-   *
* Mappe. Die Felder der gewünschten Tabelle werden eingelsen sowie die*
* Daten selber. Dabei ist es möglich, den Einlesebereich zu begrenzen *
*---------------------------------------------------------------------*
FORM upro-daten_import.

* wurde überhaupt ein Tabellenname vergeben
 CHECK NOT tabname IS INITIAL.

* Felder und Struktur zur Tabelle einlesen
 PERFORM upro-daten_import_felder.

* die Tabelle umfaßt wieviel Spalten
 DESCRIBE TABLE wktab_dd03l LINES anzahl_spalten_in.
 IF anzahl_spalten_in > 0.
* eingebener Tabellenname ist tatsächlich eine Tabelle.

* generieren eines Objektes der Struktur der ausgewählten Tabelle.
* Damit kann auf jedes Feld in der Stuktur referenziert werden. Die
* Feldname werden etwas später in diesem Unterprogramm eingelesen und
* können in Kombination abgeglichen werden
   CREATE DATA dref TYPE (tabname).
 ELSE.
* Keine R/3 Tabelle, Abbruch und raus
   MESSAGE i001 WITH tabname.
   EXIT.
 ENDIF.

 ASSIGN dref->* TO <fs_tabelle>.
 DESCRIBE FIELD <fs_tabelle> LENGTH laenge_tabelle.
 DESCRIBE FIELD wa_input     LENGTH laenge_input.
 IF laenge_tabelle > laenge_input.
* Struktur der einzulesenden Tabelle kann nicht komplett abgebildet
* werden. Die einzulesende Tabellenstruktur ist zu lang
   MESSAGE i002 WITH tabname laenge_input.
* könnte auch weiterlaufen mit abschneiden, aber Abbruch
   EXIT.
 ENDIF.

* Input-Tabelle füllen, beachten, ob im Dynpro eine Anzahl vorgegeben
* wurde, wieviele Sätze einzulesen sind
 IF anzahl_saetze = 0.
   SELECT * FROM (tabname)
   INTO TABLE itab_input.
 ELSE.
   SELECT * FROM (tabname)
   INTO TABLE itab_input
   UP TO anzahl_saetze ROWS.
 ENDIF.
* wieviel Datensätze sind gepuffert
 DESCRIBE TABLE itab_input LINES anzahl_zeilen_in.

* Range-Bereich für die verschiedensten Formen festlegen. Diese Art der
* Festlegung wäre den CSS-Style in der HTML-Programmierung vergleichbar
 CALL METHOD spreadsheet->insert_range_dim
      EXPORTING
           name     = co_format_char
           top      = 2
           left     = 2
           no_flush = 'X'
           rows     = anzahl_zeilen_in
           columns  = anzahl_spalten_in
      IMPORTING
           error    = errors.
 APPEND errors.

* für mehrere Bereich gleichzeitig
* CALL METHOD spreadsheet->insert_ranges

* Formatierung eines Bereiches in der Excel-Mappe vornehmen
* Wert für "typ"
* 0 = Standard (auch führende Nullen werden ausgegeben)
* 1 = Zahl mit 2 Dezimalstellen, wenn nicht anders angegeben
* 2 = Exponential-Zahl. 115 wird als 1,15 E+02 dargestellt
* 3 = Prozentwert
* 4 = Datum
 CALL METHOD spreadsheet->set_format
      EXPORTING
           no_flush  = 'X'           "sammeln und noch keine Anzeige
           rangename = co_format_char
           typ       = 0             "Textformat"
           currency  = ' '
*           DECIMALS  = 4
      IMPORTING
           error     = errors.
 APPEND errors.

* Farbe setzen
* Vordergrundfarbe 0/1= schwarz, 2= weiß, 3= rot, 4= grün, 5= blau
* 6= gelb,7= mangenta,8= cyano,9= braun,10= dunkelgrün,11=dunkelblau ...
 CALL METHOD spreadsheet->set_color
      EXPORTING
           no_flush  = 'X'
           rangename = co_format_char
           back      = 6
           front     = 21
      IMPORTING
           error     = errors.
 APPEND errors.

* nächsten Bereich aufbauen
* Titelzeile und Überschriften festlegen plus das Element für den
* Kommentar und alles in einer anderen Farbe
 COMPUTE anzahl_i = anzahl_spalten_in + 1.

 CALL METHOD spreadsheet->insert_range_dim
      EXPORTING
           no_flush = 'X'
           name     = co_format_titel
           top      = 1
           left     = 1
           rows     = 1
           columns  = anzahl_i
      IMPORTING
           error    = errors.
 APPEND errors.

 CALL METHOD spreadsheet->set_color
      EXPORTING
           no_flush  = 'X'
           rangename = co_format_titel
           back      = '25'
           front     = '2'
      IMPORTING
           error     = errors.
 APPEND errors.

* Spalte 1, Bereich für eigene Kommentare
 CALL METHOD spreadsheet->insert_range_dim
      EXPORTING
           no_flush = 'X'
           name     = co_format_kommentar
           top      = 2
           left     = 1
           rows     = anzahl_zeilen_in
           columns  = 1
      IMPORTING
           error    = errors.
 APPEND errors.

ENDFORM.

*---------------------------------------------------------------------*
*               D A T E N _ I M P O R T _ F E L D E R                 *
*---------------------------------------------------------------------*
* Zu der gewünschten Tabelle die Felder einlesen und die Zusatzdaten  *
* dafür bereitstellen. Die Felder werden benötigt, um die unterschied-*
* lichen Strukturen, die alle über ein einheitliches Caharakter-Feld  *
* laufen, aufschlüsseln zu können und den Inhalt zu dem gefragten Feld*
* liefern zu können                                                   *
*---------------------------------------------------------------------*
FORM upro-daten_import_felder.

* ausgewählte Tabelle beinhaltet welche Felder
 DATA:
   p_feld      TYPE rfc_fields,
   ptab_felder TYPE TABLE OF rfc_fields.

 FIELD-SYMBOLS:
   <fs_feld> LIKE p_feld.

*= = = = = = = = = = = = = =

 CLEAR:   wktab_dd03l, ptab_felder, wa_schl_offset.
 REFRESH: wktab_dd03l, ptab_felder.

* dieser Join hat Mehrfachfunktion.
* 1.) ist der Eingabewert im Data Dictionary bekannt
* 2.) ist der Eingabewert tatsächlich eine echte Tabelle
* 3.) ist die Tabelle und deren Felder aktiv
* 4.) gebraucht werden die Schlüsselfelder der Tabelle
 SELECT
   ij_03~fieldname
   ij_03~position
   ij_03~keyflag
   ij_03~rollname
   ij_03~intlen
   ij_03~inttype
 FROM       dd02l AS ij_02
 INNER JOIN dd03l AS ij_03
 ON    ij_02~tabname   = ij_03~tabname
 INTO TABLE wktab_dd03l
 WHERE ij_02~tabname   =  tabname
 AND   ij_02~as4local  =  'A'       "nur aktive Versionen Tabelle
 AND   ij_02~as4vers   =  '0000'
 AND   ij_02~tabclass  =  'TRANSP'  "muß eine transparente Tabelle sein
 AND   ij_03~fieldname => 'A'       "nur echte Felder
 AND   ij_03~fieldname <= 'Z'
 AND   ij_03~as4local  =  'A'       "nur aktive Versionen Felder
 AND   ij_03~as4vers   =  '0000'.

 IF sy-subrc = 0.
* Berechnung des Offsets über den Baustein ist sicherer
   CALL FUNCTION 'RFC_GET_STRUCTURE_DEFINITION'
        EXPORTING
             tabname          = tabname
        TABLES
             fields           = ptab_felder
        EXCEPTIONS
             table_not_active = 1
             OTHERS           = 2.
 ENDIF.

 LOOP AT ptab_felder ASSIGNING <fs_feld>.
   LOOP AT wktab_dd03l ASSIGNING <fs_dd03l>
   WHERE fieldname = <fs_feld>-fieldname.
* durch Include oder Append kann sich die Zählung verschoben haben, das
* hat der Baustein aber alles bereits inklusive, somit diese Daten über-
* nehmen
     MOVE <fs_feld>-position TO <fs_dd03l>-position.
     MOVE <fs_feld>-offset   TO <fs_dd03l>-offset.
* Offset für den Schlüssel berechnen. Die Schlüsselfelder sind immer
* hintereinander und beginnen in der Struktur bei Position 0.
     IF <fs_dd03l>-keyflag = 'X'.
       p_feld-offset = <fs_feld>-offset + <fs_feld>-intlength.
       IF p_feld-offset > wa_schl_offset.
         MOVE p_feld-offset TO wa_schl_offset.
       ENDIF.
     ENDIF.

* Kurztext zum Feld dazumischen
     IF <fs_dd03l>-rollname IS INITIAL.
* keine Referenz auf eine Domäne, den eingegebenen Text zum Feld in der
* Tabelle lesen
       SELECT SINGLE ddtext FROM dd03t INTO <fs_dd03l>-ddtext
       WHERE tabname    = tabname
       AND   ddlanguage = sy-langu
       AND   as4local   = 'A'
       AND   fieldname  = <fs_dd03l>-fieldname.
     ELSE.
* Referenz auf eine Domäne, den Domänentext einlesen
       SELECT SINGLE ddtext FROM dd04t INTO <fs_dd03l>-ddtext
       WHERE rollname   = <fs_dd03l>-rollname
       AND   ddlanguage = sy-langu
       AND   as4local   = 'A'
       AND   as4vers    = '0000'.
     ENDIF.
     IF sy-subrc <> 0.
       CLEAR <fs_dd03l>-ddtext.
     ENDIF.
   ENDLOOP.
 ENDLOOP.

* Sortieren der Feldertabelle, daß das Ergebnis des Vergleiches auch so
* angezeigt wird, wie es auch in der DDIC-Tabelle in der SE11 geschieht
 SORT wktab_dd03l BY position.

* Sicherheitsprüfung, ob Schlüssellänge ausreichend ist. Sollte beachtet
* werden, da es keine Abfrage beim Exportieren gibt, welche den Offset
* gegen die Maximallänge prüft
 DESCRIBE FIELD wa_out_schl LENGTH laenge_out_schl.
 IF wa_schl_offset > laenge_out_schl.
   MESSAGE e010(zc) WITH 'Schlüssellänge überschritten'.
 ENDIF.

* Data-Objekt generieren und prüfen ob die Tabelle eine R/3 Tabelle ist
* Catch-Befehl klappt nicht immer
* CATCH SYSTEM-EXCEPTIONS
*       create_data_unknown_type = 1
*       others = 2.
* ENDCATCH.

ENDFORM.

*---------------------------------------------------------------------*
*                 D A T E N _ I M P O R T _ T I T E L                 *
*---------------------------------------------------------------------*
* Titelzeile in Excel. Dafür die oberste Zeile nutzen. Die Texte erge-*
* ben sich für die Domänentexte oder die Texte, welche zum selbst     *
* generierten Feld in der Tabelle eingetippt wurden                   *
*---------------------------------------------------------------------*
FORM upro-daten_import_titel.

 DATA:
   wa_excel_titel     TYPE soi_generic_item,
   itab_excel_titel   TYPE soi_generic_table.

*= = = = = = = = = = =

 CLEAR:   itab_bereich, itab_excel_titel, wa_bereich, wa_excel_titel.
 REFRESH: itab_bereich, itab_excel_titel.

 wa_bereich-name    = co_format_titel.
 wa_bereich-rows    = 1.
 wa_bereich-columns = anzahl_spalten_in + 1.
 APPEND wa_bereich TO itab_bereich.

* Excel typische SAP Tabelle aufbauen für Zeile 1
 MOVE 1 TO z_excel_reihe.
 MOVE z_excel_reihe TO wa_excel_titel-row.

 CONCATENATE 'R/3 Tabelle'
             tabname
             '- Kommentar'
             INTO wa_excel_titel-value SEPARATED BY space.

 MOVE 1 TO z_excel_spalte.
 MOVE z_excel_spalte TO wa_excel_titel-column.
 APPEND wa_excel_titel TO itab_excel_titel.

 LOOP AT wktab_dd03l ASSIGNING <fs_dd03l>.
* Text zum aktuellen Feld. Tabelle liegt sortiert vor
   MOVE <fs_dd03l>-ddtext TO wa_excel_titel-value.
* Spaltenzähler berechnen. Die Kommentarspalte beachten, deswegen
* Offset um 1 erhöhen
   COMPUTE z_excel_spalte = <fs_dd03l>-position + 1.
   MOVE z_excel_spalte TO wa_excel_titel-column.

   APPEND wa_excel_titel TO itab_excel_titel.
 ENDLOOP.

* Überschriftszeile ausgeben (der Bereich grenzt ein, welche Zeilen /
* Spalten gefüllt werden), Referenz auf die SAP-Excel-Tabellen-
* Definition
 CALL METHOD spreadsheet->set_ranges_data
      EXPORTING
           no_flush = 'X'
           ranges   = itab_bereich
           contents = itab_excel_titel
      IMPORTING
           error    = errors.
 APPEND errors.

 FREE itab_excel_titel.

ENDFORM.

*---------------------------------------------------------------------*
*                   D A T E N _ I M P O R T _ B S                     *
*---------------------------------------------------------------------*
* ein eingelesene Tabelle an den Bildschirm bringen. Der Container ist*
* vorbereitet, die Daten sind eingelesen, nun nur noch den Container  *
* mit Leben füllen. BS = Bildschirm                                   *
*---------------------------------------------------------------------*
FORM upro-daten_import_bs.

* Die Referenz ist  diesmal die interne Tabelle selber und keine umge-
* schiftete, somit eine andere Methode, aber es muß die Struktur in
* Form der Tabelle mitgegeben werden, da sonst die Trennzeichen nicht
* gesetzt werden  können
 CALL METHOD spreadsheet->insert_one_table
      EXPORTING
           data_table = itab_input
           ddic_name  = tabname
           rangename  = co_format_char
           no_flush   = 'X'
           wholetable = 'X'
      IMPORTING
           error      = errors.
 APPEND errors.

* Anpassen der Spalte an den breitesten Eintrag der Spalte
 CALL METHOD spreadsheet->fit_widest
      EXPORTING
           name     = space
           no_flush = 'X'.

* in diesem SAP Beispielprogramm sollen aber gerade in den Spalten, die
* hier geschützt würden, Veränderungen erfolgen. Somit ist diese
* Methode kommentiert

* Bereich gegen Eingaben schützen
*  CALL METHOD spreadsheet->protect_range
*       EXPORTING
*            name     = co_format_char
*            protect  = 'X'
*            no_flush = 'X'
*       IMPORTING
*            error    = errors.
*  APPEND errors.

* die automatisch erstellte Queue nun aktivieren und anzeigen
 CALL METHOD c_kontrolle->set_focus
      EXPORTING
           no_flush = ' '
      IMPORTING
           error    = errors.
 APPEND errors.

 LOOP AT errors.
   CALL METHOD errors->raise_message
        EXPORTING
             type = 'E'.
 ENDLOOP.
 FREE errors.

 REFRESH: itab_bereich.
 CLEAR:   itab_bereich, wa_bereich.

ENDFORM.

*---------------------------------------------------------------------*
*                       D A T E N _ E X P O R T                       *
*---------------------------------------------------------------------*
* Die Daten sind in Exel bearbeitet und sollen nun wieder an das SAP  *
* zurücktransferiert werden.                                          *
*---------------------------------------------------------------------*
* Hinweis: Spalte 1 des Bereiches muß dem ersten Feld der Tabelle     *
* entsprechen, keine Lücken in den Spalten, keine neuen Spalten, keine*
* Spalten gelöscht. Die Spalte dient den Suchalgorithmus für das Feld *
* als Referenz.                                                       *
*---------------------------------------------------------------------*
FORM upro-daten_export.

 CLEAR:   wa_bereich, wa_excel_out, wa_output, wa_out_schl,
          anzahl_zeilen_out, anzahl_spalten_out, zw_out_row,
          itab_excel_out, itab_output, itab_out_schl, itab_bereich.
 REFRESH: itab_excel_out, itab_output, itab_out_schl, itab_bereich.

 wa_bereich-name = co_format_char.
 APPEND wa_bereich TO itab_bereich.
* wa_bereich-name = co_format_num.
* APPEND wa_bereich TO itab_bereich.
* wa_bereich-name = co_format_dats.
* APPEND wa_bereich TO itab_bereich.

* alle Daten im Bereich von "co_format_char" einlesen
* durch die Festlegung auf den Bereich, daß nur dort Daten wieder
* eingelesen werden, sind Prüfungen oder EIngaben außerhalb des
* Bereiches ohne Wirkung
 CALL METHOD spreadsheet->get_ranges_data
      EXPORTING
           all      = ' '
           no_flush = ' '
      IMPORTING
           contents = itab_excel_out
      CHANGING
           ranges   = itab_bereich.

* check no errors occured
 CALL METHOD error->raise_message
      EXPORTING
           type = 'E'.

 LOOP AT itab_excel_out ASSIGNING <fs_excel>.
* neue Reihe, war vorher sortiert worden. Aktuelle Zeile fortschreiben
   IF  <fs_excel>-row <> zw_out_row
   AND zw_out_row > 0.
     PERFORM upro-daten_export_element.
   ENDIF.
* umwandeln der Spaltenzahl auf ein echtes NUMC-Feld, wegen der
* Abfragen im Vergleich. Die Reihe braucht nicht betrachtet werden
   SHIFT <fs_excel>-column RIGHT DELETING TRAILING ' '.
   REPLACE ' ' WITH '0' INTO <fs_excel>-column.
   REPLACE ' ' WITH '0' INTO <fs_excel>-column.
   REPLACE ' ' WITH '0' INTO <fs_excel>-column.

* die Spalten- und Reihenanzahl feststellen. Sollte sich die Spalten-
* anzahl verändert haben, kann kein direkter Vergleich stattfindet, da
* in diesem Beispielprogramm ein Spaltenflag mitgeschleift wurde
   IF <fs_excel>-column > anzahl_spalten_out.
     MOVE <fs_excel>-column TO anzahl_spalten_out.
   ENDIF.

* wandeln von der Excel-Enlesetabelle auf eine Strukturtabelle ohne
* Formatierung sowie einer Schlüsseltabelle. Dadurch kann der Doppel-
* loop bei Vergleich sich erspart werden => System wird schneller
   MOVE <fs_excel>-column TO z_excel_spalte.

* feststellen, was die aktuelle Spalte für ein Feld ist und wo in der
* Struktur es sich befindet
   READ TABLE wktab_dd03l ASSIGNING <fs_dd03l>
   WITH KEY position = z_excel_spalte.
   IF sy-subrc = 0.
     IF <fs_dd03l>-keyflag = 'X'.
       MOVE <fs_excel>-value(<fs_dd03l>-intlen)
            TO: wa_output+<fs_dd03l>-offset(<fs_dd03l>-intlen),
                wa_out_schl-t+<fs_dd03l>-offset(<fs_dd03l>-intlen).
     ELSE.
       MOVE <fs_excel>-value(<fs_dd03l>-intlen)
            TO wa_output+<fs_dd03l>-offset(<fs_dd03l>-intlen).
     ENDIF.
   ENDIF.

   MOVE <fs_excel>-row TO zw_out_row.
 ENDLOOP.

 PERFORM upro-daten_export_element.

* Zellen ohne Inhalt löschen, diese blähen die interne Tabelle
* unnötig auf und werden nicht gebraucht. Erst jetzt löschen, wegen der
* Berechnung der Spaltenzahl, falls die letzten Spalten immer initial
* haben, kann es so zu Abweichungen kommen
 DELETE itab_excel_out
 WHERE value IS initial.

ENDFORM.

*---------------------------------------------------------------------*
*               D A T E N _ E X P O R T _ E L E M E N T               *
*---------------------------------------------------------------------*
* eine Zeile der typischen Excel-SAP-Definition ist aufgebaut und kann*
* an die internen Tabellen der Ausgabe angehangen werden. Die eine    *
* Tabelle beinhaltet die Wertefelder, diese ist unkritisch. Die andere*
* Tabelle beinhaltet nur die Schlüsselbegriffe, diese Tabelle ist     *
* jedoch mit einem Hash-Zugriff ausgestattet, somit erst prüfen, ob   *
* der Eintrag nicht schon vorhanden ist                               *
*---------------------------------------------------------------------*
FORM upro-daten_export_element.

* ohne Schlüssel keine Aktion.
 CHECK NOT wa_out_schl-t IS INITIAL.

* Datensatz Schlüssel und Wertefelder in interne Tabelle anhängen
 APPEND wa_output TO itab_output.
* Referenz merken, auf welchen Datensatz der Schlüsselsatz zeigen soll.
* Es sollte 1:1 sein, aber Excel stellt keinen doppelten Schlüssel fest
 MOVE sy-tabix TO wa_out_schl-idx.

* schauen, ob der Schlüssel schonmal im Umlauf war
 READ TABLE itab_out_schl TRANSPORTING NO FIELDS
 WITH TABLE KEY t = wa_out_schl-t.
 IF sy-subrc <> 0.
* Schlüssel schon nicht vorhanden, interne Tabelle auffüllen
   INSERT wa_out_schl INTO TABLE itab_out_schl.
 ENDIF.

 CLEAR: wa_output, wa_out_schl.

ENDFORM.

*---------------------------------------------------------------------*
*                      P R O T O K O L L _ M I T                      *
*---------------------------------------------------------------------*
* Vergleich der Eingabetabelle (Import) mit der Ausgabetabelle        *
* (Export). Dieses Unterprogramm vergleicht MIT einer umgewandelteten *
* Ausgabetabelle, d.h. das Programm hat zuvor die Excel-Mappe in eine *
* Strukturtabelle gewandelt, so daß auch die Struktur direkt vergli-  *
* chen werden kann, anstatt einzelne Elemente                         *
*---------------------------------------------------------------------*
* Vergleichsform: Eingangstabelle als Basis vergleicht die Ausgangs-  *
* tabelle                                                             *
*---------------------------------------------------------------------*
FORM upro-protokoll_mit.

 LEAVE TO LIST-PROCESSING.

 SORT itab_excel_out BY row column.

* Eingangstabelle als Basis für die Anzeige wählen
 LOOP AT itab_input INTO wa_input.
* Schlüssel extra speichern
   MOVE wa_input(wa_schl_offset) TO wa_in_schl.

* prüfen, ob es den Schlüssel der Eingabetabelle (für den Import) auch
* noch in der Ausgabetabelle (Export) gibt
   READ TABLE itab_out_schl INTO wa_out_schl
   WITH TABLE KEY t = wa_in_schl.
   IF sy-subrc <> 0.
     CLEAR: wa_out_schl, wa_output.
* Datensatz gelöscht, Zeile rot markiert ausgeben
     FORMAT COLOR 6 ON INTENSIFIED OFF.
     WRITE: / wa_in_schl(wa_schl_offset).
     FORMAT COLOR 6 OFF INTENSIFIED ON.
   ELSE.
* Schlüssel gefunden, dieses in der Ausgabetabelle sichern
     MOVE 'X' TO wa_out_schl-gef.
     MODIFY TABLE itab_out_schl FROM wa_out_schl TRANSPORTING gef.
* Datensatz auch mit den Empfansgfeldern nachlesen
     READ TABLE itab_output INTO wa_output INDEX wa_out_schl-idx.
     IF sy-subrc <> 0.
       CLEAR: wa_output.
     ENDIF.

     WRITE: / wa_in_schl(wa_schl_offset).

     PERFORM upro-protokoll_empfangsfelder.
   ENDIF.
 ENDLOOP.

* Ausgangstabelle durchforsten auf Schlüssel, auf die noch nicht
* abgefragt wurde. Diese sind neu hinzugekommen
 LOOP AT itab_out_schl INTO wa_out_schl
 WHERE gef = ' '.
   FORMAT COLOR 5 ON INTENSIFIED OFF.
   WRITE: / wa_out_schl-t(wa_schl_offset).
   FORMAT COLOR 5 OFF INTENSIFIED ON.
 ENDLOOP.

ENDFORM.

*---------------------------------------------------------------------*
*                     P R O T O K O L L _ O H N E                     *
*---------------------------------------------------------------------*
* Vergleich der Ausgabetabelle (Export) mit der Eingabetabelle        *
* (Import). Dieses Unterprogramm vergleicht OHNE eine umgewandeltete  *
* Ausgabetabelle. Somit entfällt die Zwischentabelle. Nachteil ist die*
* Ausrichtungsform (Ausgangstabelle als Basis), wenn die Eingabeta-   *
* belle als Basis hier genommen würde, wäre das ein doppelter Loop => *
* zu jedem Eingabedatensatz müsste komplett die Ausgabetabelle durch- *
* sucht werden, die jedoch nut felderweise vorliegt. Das wäre ein     *
* Performancefresser und somit bei großen Tabelle nicht effektiv.     *
*---------------------------------------------------------------------*
* Vergleichsform: Ausgangstabelle als Basis vergleicht die Eingangs-  *
* tabelle                                                             *
*---------------------------------------------------------------------*
* Hinweis: Spalte 1 des Bereiches muß dem ersten Feld der Tabelle     *
* entsprechen. Siehe auch Beschreibung Export.                        *
*---------------------------------------------------------------------*
FORM upro-protokoll_ohne.

 LEAVE TO LIST-PROCESSING.

 SORT itab_excel_out BY row column.

 CLEAR:   zw_out_row, wa_in_out_gleich, wa_out_schl, wa_output,
          itab_ohne_schl.
 REFRESH: itab_ohne_schl.

* die gesamte Ausgabetabelle durchgehen, alle Schlüssel und Inhalte
* aufbauen und prüfen, es einen Schlüssel gibt, der dem der Eingabe-
* tabelle entspricht
 LOOP AT itab_excel_out INTO wa_excel_out
 WHERE column <> '9999'.
   IF  wa_excel_out-row <> zw_out_row
   AND zw_out_row > 0.
     PERFORM upro-protokoll_ohne_vgl.
   ENDIF.
* wandeln von der Excel-Enlesetabelle auf eine Strukturtabelle ohne
* Formatierung sowie einer Schlüsseltabelle. Dadurch kann der Doppel-
* loop bei Vergleich sich erspart werden => System wird schneller
   MOVE wa_excel_out-column TO z_excel_spalte.

* feststellen, was die aktuelle Spalte für ein Feld ist und wo in der
* Struktur es sich befindet. Bei Schlüssel zusätzlich auch den
* Schlüssel auffüllen für den Reihenvergleich
   READ TABLE wktab_dd03l ASSIGNING <fs_dd03l>
   WITH KEY position = z_excel_spalte.
   IF sy-subrc = 0.
     IF <fs_dd03l>-keyflag = 'X'.
       MOVE wa_excel_out-value(<fs_dd03l>-intlen)
            TO: wa_output+<fs_dd03l>-offset(<fs_dd03l>-intlen),
                wa_out_schl-t+<fs_dd03l>-offset(<fs_dd03l>-intlen).
     ELSE.
       MOVE wa_excel_out-value(<fs_dd03l>-intlen)
            TO wa_output+<fs_dd03l>-offset(<fs_dd03l>-intlen).
     ENDIF.
   ENDIF.

   MOVE wa_excel_out-row TO zw_out_row.
 ENDLOOP.

 PERFORM upro-protokoll_ohne_vgl.

* nun noch die Eingangstabelle durchlaufen und Schlüssel suchen, welche
* nicht mehr in der Ausgangstabelle vorhanden sind
 LOOP AT itab_input INTO wa_input.
   READ TABLE itab_ohne_schl TRANSPORTING NO FIELDS
   WITH TABLE KEY idx = sy-tabix.
   IF sy-subrc <> 0.
* Datensatz gelöscht, Zeile rot markiert ausgeben
     FORMAT COLOR 6 ON INTENSIFIED OFF.
     WRITE: / wa_input(wa_schl_offset).
     FORMAT COLOR 6 OFF INTENSIFIED ON.
   ENDIF.
 ENDLOOP.

ENDFORM.

*---------------------------------------------------------------------*
*                P R O T O K O L L _ O H N E _ V G L                  *
*---------------------------------------------------------------------*
* alle Spalten zu einer Reihe in der Ausgangstabelle sind durchgegan- *
* gen worden und der Datensatz ist in seiner Struktur aufgebaut. Diese*
* Struktur und den Schlüssel gegen die Eingangstabelle laufen lassen. *
* Sollte der Schlüssel auch in der Eingangstabelle gefunden sein, dann*
* einen Vergleich auf die einzelnen Wertefelder durchführen und Diffe-*
* renzen feststellen und anzeigen.                                    *
*---------------------------------------------------------------------*
* =>] wa_out_schl: Schlüssel der Reihe der Ausgabetabelle             *
*---------------------------------------------------------------------*
FORM upro-protokoll_ohne_vgl.

 CLEAR: wa_in_out_gleich.

* gegen die Eingangstabelle prüfen
 LOOP AT itab_input INTO wa_input.
   IF wa_out_schl-t = wa_input(wa_schl_offset).
* die Schlüsseltabelle für "ohne-Protokoll-Eingangstabelle" erweitern
     MOVE sy-tabix TO wa_ohne_schl-idx.
     READ TABLE itab_ohne_schl TRANSPORTING NO FIELDS
     WITH TABLE KEY idx = wa_ohne_schl-idx.
     IF sy-subrc <> 0.
       INSERT wa_ohne_schl INTO TABLE itab_ohne_schl.
     ENDIF.

* da ein Treffer gefunden wurde, braucht nicht weiter kontrolliert zu
* werden. Abbruch.
     MOVE 'X' TO wa_in_out_gleich.

     EXIT.
   ENDIF.
 ENDLOOP.

 IF wa_in_out_gleich IS INITIAL.
* Ausgangstabellenschlüssel ist nicht in der Eingabetabelle vorhanden
* Schlüssel ist neu angelegt worden
   FORMAT COLOR 5 ON INTENSIFIED OFF.
   WRITE: / wa_out_schl-t(wa_schl_offset).
   FORMAT COLOR 5 OFF INTENSIFIED ON.
 ELSE.
   WRITE: / wa_out_schl-t(wa_schl_offset).

   PERFORM upro-protokoll_empfangsfelder.
 ENDIF.

 CLEAR: wa_out_schl, wa_output.

ENDFORM.

*---------------------------------------------------------------------*
*           P R O T O K O L L _ E M P F A N G S F E L D E R           *
*---------------------------------------------------------------------*
* Struktur der Eingangstabelle und der Ausgangstabelle gegeneinander  *
* vergleichen. Beide Strukturen sind aufgebaut, die Felder bekannt.   *
* Es werden nur die Empfangsfelder verglichen, die Schlüsselfelder    *
* wurden bereits zuvor behandelt.                                     *
*---------------------------------------------------------------------*
* Hinweis: im dieser Version findet KEIN Umwandeln numerischer oder   *
* Datumsfelder statt. Der korrekte Abgleich kann so nur bei CHAR-Fel- *
* dern erfolgen                                                       *
*---------------------------------------------------------------------*
FORM upro-protokoll_empfangsfelder.

* Abgleich nur noch auf Empfangsfelder
 LOOP AT wktab_dd03l ASSIGNING <fs_dd03l>
 WHERE keyflag <> 'X'.
   IF wa_output+<fs_dd03l>-offset(<fs_dd03l>-intlen)
      <> wa_input+<fs_dd03l>-offset(<fs_dd03l>-intlen).
* Eingabe- und Ausgabewert sind unterschiedlich, gelb anzeigen
     FORMAT COLOR 3 ON INTENSIFIED OFF.
     WRITE: '.'.
     FORMAT COLOR 3 OFF INTENSIFIED ON.
   ELSE.
* sind gleich, neutral bleiben
     WRITE: '.'.
   ENDIF.
 ENDLOOP.

ENDFORM.

***** Ende *****
* Copyright BJH Software, Datei überarbeitet am: 23.7.2005