SAP Materialstamm mit BAPI über Excel Tabelle ändern
Der Fall wird vermutlich in fast allen Unternehmen irgendwann einmal auftreten: Es gibt eine Prozessänderung im SAP und nun muss der Materialstamm gepflegt werden. Häufig werden dabei die Daten in Excel vorbereitet und sollen dann irgendwie in das SAP gelangen. Dazu gibt es dann natürlich diverse Wege wie Batch-Input Mappen, LSMW oder auch einen eigenen Report.
Welcher Weg der Beste ist bleibt wohl immer ein Diskussionsthema. In diesem Beitrag möchte ich einmal zeigen, wie über einen eigenen Report mit BAPIs der Materialstamm geändert werden kann. Ich werde dazu die Produkthierarchie in Materialien einfügen.
Datenquelle CSV aus Excel
Als Datenquelle dient eine CSV Datei aus Exel. In diesem Fall besteht diese nur aus zwei Spalten, der Materialnummer und der Produkthierarchie. Da die Produkthierarchie nur bei den Grunddaten gepflegt werden soll sind keine weiteren Informationen nötig.
Bei der Pflege von Feldern in anderen Sichten müssen natürlich je nach Sicht weitere Org-Daten mitgegeben werden.
Meine CSV-Datei sieht also wie folgt aus:
Materialnummer; Produkthierarchie;
Report zum Ändern der Materialstammdaten
Ich werde den Report hier in einzelnen Schritten erläutern. Am Ende werde ich den gesamten Report noch einmal eintragen.
Es werden diverse Datenfelder benötigt. Diese Speichern hauptsächlich den Dateiinhalt der CSV-Datei sowie die Nachrichten der BAdis um diese später auswerten zu können.
Wichtig ist hierbei die Struktur der CSV-Datei auch in SAP abzubilden. Dazu kann ein Datenelement angelegt werden oder man definiert es über die TYPES-Anweisung direkt im Report. Zweiteres lässt sich im Report schnell ändern, sollte die Struktur aber in anderen Reports ebenfalls zum Einsatz kommen, dann empfiehlt es sich eine Struktur und einen Tabellentyp anzulegen.
TABLES: mara. * Struktur der CSV Datei TYPES: BEGIN OF csv_struc, matnr LIKE mara-matnr, prdha LIKE mara-prdha, END OF csv_struc. * Datenfelder DATA: lt_import TYPE TABLE OF csv_struc, "CSV als ITab ls_line TYPE csv_struc, "Einzelne Zeile der CSV rawdata TYPE truxs_t_text_data, "Rohdaten der CSV lv_filename TYPE string, "Dateiname lt_filetable TYPE filetable, "Tabelle von Dateinamen lf_rc TYPE i, "Returncode beim Auswählen der dAtei ls_file TYPE file_table, "Einzelne Datei lt_return LIKE bapiret2, "Rückgabetabelle der Badis lt_returnmsg TYPE TABLE OF bapi_matreturn2, "Nachrichten aus Badi ls_returnmsg TYPE bapi_matreturn2, "Einzelne Nachricht für Badi lv_matnr TYPE matnr, "Materialnummer ls_head LIKE bapimathead, "Kopfdaten Badi ls_cldata LIKE bapi_mara, "Detaildaten Badi ls_cldatax LIKE bapi_marax, "Detaildaten Badi Änderungsmarkierung lo_alv TYPE REF TO cl_salv_table, "ALV Objekt lo_functions TYPE REF TO cl_salv_functions_list. "ALV Optionen
Als nächstes müssen wir dem Benutzer die Möglichkeit geben eine Datei auszuwählen. Dafür gibt es die Methode “File_open_dialog” der Klasse “cl_gui_frontend_server”. Hier kann man auch schon mal die Dateiendung vorschlagen.
CALL METHOD cl_gui_frontend_services=>file_open_dialog EXPORTING window_title = 'CSV Datei auswählen' file_filter = '*.csv' multiselection = abap_false CHANGING file_table = lt_filetable rc = lf_rc EXCEPTIONS file_open_dialog_failed = 1 cntl_error = 2 error_no_gui = 3 not_supported_by_gui = 4 OTHERS = 5. * Prüfen ob alles funktioniert hat IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno DISPLAY LIKE 'E' WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. EXIT. ENDIF.
Der “file_open_dialog” ermöglicht es mehrere Dateien gleichzeitig auszuwählen. Dass wollen wir nicht, daher nehmen wir die erste Datei aus der Tabelle und prüfen auch gleich ob überhaupt etwas gefunden wurde.
READ TABLE lt_filetable INTO ls_file INDEX 1. CHECK sy-subrc = 0. lv_filename = ls_file-filename.
Nun da die Datei ausgewählt wurde muss sie noch hochgeladen werden. Das Format nach dem Upload lässt sich aber nicht gut verarbeiten, daher konvertieren wir den Inhalt noch in eine Interne Tabelle.
CALL FUNCTION 'GUI_UPLOAD' EXPORTING filename = lv_filename TABLES data_tab = rawdata 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 OTHERS = 17. * Die Daten werden nun in ein Format umgewandelt mit dem wir arbeiten könne (ITAB) CALL FUNCTION 'TEXT_CONVERT_CSV_TO_SAP' EXPORTING i_field_seperator = ';' * i_line_header = i_tab_raw_data = rawdata * i_filename = 'd:\myfile1.txt' TABLES i_tab_converted_data = lt_import EXCEPTIONS conversion_failed = 1 OTHERS = 2.
In einer Schleife die wir über diese zuvor erstellte interne Tabelle laufen lassen werden die Änderungen für den BAPI_MATERIAL_SAVEDATA durchgeführt.
Dabei muss dem BAPI im Kopf mitgeteilt werden, welches Material geändert werden muss und welche Sicht gewünscht ist. In dem Fall hier ist das nur die Grunddatensicht. Anschließend werden die Felder dieser Sicht geändert. Dazu gibt es immer zwei Strukturen. Eine Struktur für die tatsächlichen Inhalte und eine weitere Struktur die auf X endet für eine Markierung der geänderten Felder. Bitte also darauf achten, dass immer beide Strukturen geändert werden.
Zur Sicherheit prüfen wir auch, ob das Material überhaupt existiert. Wenn das nicht der Fall ist, dann fügen wir in der Rückgabetabelle für die BAPI-Nachrichten einen Eintrag hinzu. Über diesen Eintrag können wir später dann die Materialien mit Problemen finden.
LOOP AT lt_import INTO ls_line. * Prüfen ob das Material existiert, könnte aber auch über BAPI_MATERIAL_GET_ALL erfolgen, falls man z.B. mehr Daten braucht SELECT SINGLE matnr FROM mara INTO lv_matnr WHERE matnr EQ ls_line-matnr. IF sy-subrc EQ 0. * Kopfdaten setzen. Hier wird markiert, welche Sichten geändert werden sollen ls_head-material = ls_line-matnr. ls_head-basic_view = 'X'. * Detaildaten: Hier werden die tatsächlichen Daten eingefügt. Zusätzlich sind diese als "geändert" zu markieren in der X-Tabelle ls_cldata-prod_hier = ls_line-prdha. ls_cldatax-prod_hier = 'X'. * Geänderte Daten speichern CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA' EXPORTING headdata = ls_head clientdata = ls_cldata clientdatax = ls_cldatax IMPORTING return = lt_return TABLES returnmessages = lt_returnmsg . ELSE. * Wenn das Material nicht gefunden wurde fügen wir eine Nachricht zur Sammlung hinzu ls_returnmsg-type = 'E'. ls_returnmsg-number = '999'. "Damit man später filtern kann CONCATENATE 'Material nicht gefunden: ' ls_line-matnr INTO ls_returnmsg-message RESPECTING BLANKS. APPEND ls_returnmsg TO lt_returnmsg. ENDIF. ENDLOOP.
Damit die Daten auch wirklich geändert werden ist ein Commit erforderlich.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
So könnten die Daten jetzt schon geändert werden. Hilfreich ist aber in vielen Fällen sicherlicht zu erfahren, ob auch alles funktioniert hat. Daher können wir am Ende noch die Nachrichten die der BAPI zurück gibt in einem ALV ausgeben. Dieser lässt sich dann später auswerten.
cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv CHANGING t_table = lt_returnmsg ). lo_functions = lo_alv->get_functions( ). lo_functions->set_all( abap_true ). lo_alv->display( ).
Das war schon alles.
Der gesamte Report
REPORT zmm_update_ph_by_file. TABLES: mara. * Struktur der CSV Datei TYPES: BEGIN OF csv_struc, matnr LIKE mara-matnr, prdha LIKE mara-prdha, END OF csv_struc. * Datenfelder DATA: lt_import TYPE TABLE OF csv_struc, "CSV als ITab ls_line TYPE csv_struc, "Einzelne Zeile der CSV rawdata TYPE truxs_t_text_data, "Rohdaten der CSV lv_filename TYPE string, "Dateiname lt_filetable TYPE filetable, "Tabelle von Dateinamen lf_rc TYPE i, "Returncode beim Auswählen der dAtei ls_file TYPE file_table, "Einzelne Datei lt_return LIKE bapiret2, "Rückgabetabelle der Badis lt_returnmsg TYPE TABLE OF bapi_matreturn2, "Nachrichten aus Badi ls_returnmsg TYPE bapi_matreturn2, "Einzelne Nachricht für Badi lv_matnr TYPE matnr, "Materialnummer ls_head LIKE bapimathead, "Kopfdaten Badi ls_cldata LIKE bapi_mara, "Detaildaten Badi ls_cldatax LIKE bapi_marax, "Detaildaten Badi Änderungsmarkierung lo_alv TYPE REF TO cl_salv_table, "ALV Objekt lo_functions TYPE REF TO cl_salv_functions_list. "ALV Optionen * Open File Dialog um die Datei auf dem PC auszuwählen CALL METHOD cl_gui_frontend_services=>file_open_dialog EXPORTING window_title = 'CSV Datei auswählen' file_filter = '*.csv' multiselection = abap_false CHANGING file_table = lt_filetable rc = lf_rc EXCEPTIONS file_open_dialog_failed = 1 cntl_error = 2 error_no_gui = 3 not_supported_by_gui = 4 OTHERS = 5. * Prüfen ob alles funktioniert hat IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE 'S' NUMBER sy-msgno DISPLAY LIKE 'E' WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. EXIT. ENDIF. * Dateiname aus Tabelle auslesen READ TABLE lt_filetable INTO ls_file INDEX 1. CHECK sy-subrc = 0. lv_filename = ls_file-filename. * Datei wurde nur ausgewählt, muss aber noch an den SAP Server übertragen werden CALL FUNCTION 'GUI_UPLOAD' EXPORTING filename = lv_filename TABLES data_tab = rawdata 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 OTHERS = 17. * Die Daten werden nun in ein Format umgewandelt mit dem wir arbeiten könne (ITAB) CALL FUNCTION 'TEXT_CONVERT_CSV_TO_SAP' EXPORTING i_field_seperator = ';' * i_line_header = i_tab_raw_data = rawdata * i_filename = 'd:\myfile1.txt' TABLES i_tab_converted_data = lt_import EXCEPTIONS conversion_failed = 1 OTHERS = 2. * Schleife über die Zeilen. Hier ggf. Überschriftenzeile beachten! LOOP AT lt_import INTO ls_line. * Prüfen ob das Material existiert, könnte aber auch über BAPI_MATERIAL_GET_ALL erfolgen, falls man z.B. mehr Daten braucht SELECT SINGLE matnr FROM mara INTO lv_matnr WHERE matnr EQ ls_line-matnr. IF sy-subrc EQ 0. * Kopfdaten setzen. Hier wird markiert, welche Sichten geändert werden sollen ls_head-material = ls_line-matnr. ls_head-basic_view = 'X'. * Detaildaten: Hier werden die tatsächlichen Daten eingefügt. Zusätzlich sind diese als "geändert" zu markieren in der X-Tabelle ls_cldata-prod_hier = ls_line-prdha. ls_cldatax-prod_hier = 'X'. * Geänderte Daten speichern CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA' EXPORTING headdata = ls_head clientdata = ls_cldata clientdatax = ls_cldatax * PLANTDATA = * PLANTDATAX = * FORECASTPARAMETERS = * FORECASTPARAMETERSX = * PLANNINGDATA = * PLANNINGDATAX = * STORAGELOCATIONDATA = * STORAGELOCATIONDATAX = * VALUATIONDATA = * VALUATIONDATAX = * WAREHOUSENUMBERDATA = * WAREHOUSENUMBERDATAX = * SALESDATA = * SALESDATAX = * STORAGETYPEDATA = * STORAGETYPEDATAX = * FLAG_ONLINE = ' ' * FLAG_CAD_CALL = ' ' * NO_DEQUEUE = ' ' * NO_ROLLBACK_WORK = ' ' IMPORTING return = lt_return TABLES * MATERIALDESCRIPTION = * UNITSOFMEASURE = * UNITSOFMEASUREX = * INTERNATIONALARTNOS = * MATERIALLONGTEXT = * TAXCLASSIFICATIONS = returnmessages = lt_returnmsg * PRTDATA = * PRTDATAX = * EXTENSIONIN = * EXTENSIONINX = . ELSE. * Wenn das Material nicht gefunden wurde fügen wir eine Nachricht zur Sammlung hinzu ls_returnmsg-type = 'E'. ls_returnmsg-number = '999'. "Damit man später filtern kann CONCATENATE 'Material nicht gefunden: ' ls_line-matnr INTO ls_returnmsg-message RESPECTING BLANKS. APPEND ls_returnmsg TO lt_returnmsg. ENDIF. ENDLOOP. * Datenübernahme muss mit Commit bestätigt werden CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'. cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv CHANGING t_table = lt_returnmsg ). lo_functions = lo_alv->get_functions( ). lo_functions->set_all( abap_true ). lo_alv->display( ).