In this Blog-post I’d like to give a few insights on how we process XLSX file by using latest ABAP, which might be quite different than in most other implementations, for example:ABAP and OLE or Excel with SAP – An Overview
By using CL_XLSX_DOCUMENT (available from 2008), it is easy to load file, workbook, sheets, rows and columns. A good example code can be found within class CL_EHFND_XLSX(available on ABAP AS 752 SP-Level 0004, Software Component S4CORE Release 102, SP 4.).
On the other hand, you can set document format by using IF_IXML_NODE, below example code show how …
lo_xlsx_doc = cl_xlsx_document=>load_document( <XSTRING of xlsx file> )
lo_workbookpart = lo_xlsx_doc->get_workbookpart( ).
lo_wordsheetparts = lo_workbookpart->get_worksheetparts( ).
lo_wordsheetpart = lo_wordsheetparts->get_part( 0 ).
lo_sheet_content = lo_wordsheetpart->get_data( ).
CREATE OBJECT lo_xml_document.
lo_xml_document->parse_xstring( lo_sheet_content ).
DATA(lo_node_datavalidation_init) = lo_node_datavalidations->clone( ).
....
DATA(lo_node_datavalidation) = lo_node_datavalidation_init->clone( ).
lo_node_datavalidation->set_name( 'dataValidation' ).
CONCATENATE ls_validation-column lv_begin_str ':' ls_validation-column lv_end_str INTO
DATA(lv_sqref).
DATA(lo_attrs_map_datavalidation) = lo_node_datavalidation->get_attributes( ).
DATA(lo_attr_sqref) = lo_attrs_map_datavalidation->get_named_item_ns( 'count' )->clone( ).
lo_attr_sqref->set_name( 'sqref' ).
lo_attr_sqref->set_value( lv_sqref ).
lo_attrs_map_datavalidation->set_named_item_ns( lo_attr_sqref ).
lo_attrs_map_datavalidation->remove_named_item_ns( 'count' ).
DATA(lo_attr_showerrormsg) = lo_attr_sqref->clone( ).
lo_attr_showerrormsg->set_name( 'showErrorMessage' ).
lo_attr_showerrormsg->set_value( '1' ).
lo_attrs_map_datavalidation->set_named_item_ns( lo_attr_showerrormsg ).
.....
Executable SE38 report.
Main functionalities of this report are:
- Download any DDIC table’s content into xlsx file
- Change value in the xlsx file
- upload xlsx file into DDIC table
- check changed value in DDIC table.
And the dynamic structure is used in this report. The code style is what I would like to present also.
User Interface
selection-screen begin of block b04 with frame title text-b04.
parameters p_exp radiobutton group radi user-command action default 'X'.
parameters p_imp radiobutton group radi.
selection-screen end of block b04.
selection-screen begin of block b01 with frame title text-b01.
parameters p_table type dd02l-tabname modif id gp1 obligatory memory id ht.
parameters p_file type localfile modif id gp2 obligatory memory id hf.
parameters p_sql type string modif id gp3.
selection-screen comment /1(75) comm.
selection-screen end of block b01.
initialization.
comm = `e.g. RLDNR = 'Y1' AND RRCTY = 'U'`.
at selection-screen on value-request for p_file.
* call function 'F4_FILENAME'
* exporting
* field_name = 'P_FILE'
* importing
* file_name = p_file.
data(title) = |Select Excel File, e.g. *.xlsx|.
data(defaultextension) = |.xlsx|.
data(filefilter) = `Excel Files (*.xlsx)|*.xlsx`.
data it_tab type filetable.
data returncode type i.
call method cl_gui_frontend_services=>file_open_dialog
exporting
window_title = title
default_extension = defaultextension
* default_filename =
* file_filter = filefilter
* with_encoding =
* initial_directory =
* multiselection =
changing
file_table = it_tab
rc = returncode
* user_action =
* file_encoding =
* exceptions
* file_open_dialog_failed = 1
* cntl_error = 2
* error_no_gui = 3
* not_supported_by_gui = 4
* others = 5
.
if sy-subrc <> 0.
* Implement suitable error handling here
endif.
read table it_tab assigning field-symbol(<selectedfilename>) index 1.
if sy-subrc = 0.
p_file = <selectedfilename>-filename.
endif.
Main Functionality
Download table content into local excel file (xlsx)
Select action “Export”
Input existing table , e.g. Table Name T000, File Full Path: c:demot000.xlsx
Change the value of non-key column in xlsx file.
start-of-selection.
try.
data(configurationhandler) = new lcl_configuration( filefullpath = conv #( p_file )
tablename = conv #( p_table )
sqlscript = p_sql ).
if p_exp = abap_true.
configurationhandler->export( ).
else.
configurationhandler->import( ).
endif.
catch lcx_configuration into data(configurationexception).
write: / configurationexception->local_text.
endtry.
Upload local excel file (*.xlsx) into table
Select action “Import”
Input existing table , e.g. Table Name T000, File Full Path: c:demot000.xlsx
Empty row will be skip, and table entries will be modified (Insert or Update). It is not possible to delete existing table entry.
start-of-selection.
try.
data(configurationhandler) = new lcl_configuration( filefullpath = conv #( p_file )
tablename = conv #( p_table )
sqlscript = p_sql ).
if p_exp = abap_true.
configurationhandler->export( ).
else.
configurationhandler->import( ).
endif.
catch lcx_configuration into data(configurationexception).
write: / configurationexception->local_text.
endtry.
Exception Handling
You will get exception of structure infliction if xlsx file is not for that table.
"check file structure, first line of excel file
data(columncount) = firstsheet->get_last_column_number_in_row( 1 ).
data column type i value 1.
"data tablecomponents type cl_abap_structdescr=>component_table.
data(tablecomponents) = me->tablestructure->get_components( ).
data invalidcolumn type string.
types: begin of columninfo,
column type i,
columnname type string,
end of columninfo.
types columnsinfo type standard table of columninfo with empty key.
data columnfromfile type columnsinfo.
do columncount times.
data(cellvalue) = firstsheet->get_cell_content(
exporting
iv_row = 1
iv_column = column ).
append initial line to columnfromfile assigning field-symbol(<columnfromfile>).
<columnfromfile>-column = column.
<columnfromfile>-columnname = cellvalue.
if line_exists( tablecomponents[ name = cellvalue ] ).
delete tablecomponents where name = cellvalue.
else.
invalidcolumn = invalidcolumn && |,{ cellvalue }|.
endif.
column = column + 1.
enddo.
data missingcolumns type string.
loop at tablecomponents reference into data(currentcomponent).
missingcolumns = missingcolumns && |, { currentcomponent->*-name }|.
endloop.
if not invalidcolumn is initial.
raise exception type lcx_configuration
exporting
text = |Find invalid columns: { invalidcolumn } |.
endif.
First row will be the table columns’ name
Local Class Definition
*&---------------------------------------------------------------------*
*& Include ZZZZ_HOME_CFG_FINE_TUNING_LCL
*&---------------------------------------------------------------------*
class lcx_configuration definition
inheriting from cx_static_check.
public section.
data local_text type string.
methods constructor importing text type string.
endclass.
class lcx_configuration implementation.
method constructor.
super->constructor( ).
local_text = text.
endmethod.
endclass.
*&---------------------------------------------------------------------*
*& Class lcl_configuration
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
class lcl_configuration definition.
public section.
data filefullpath type string.
data tablename type string.
data sqlscript type string.
data tableinfo type tadir.
data tablestructure type ref to cl_abap_structdescr.
data tabletype type ref to cl_abap_tabledescr.
data tabledata type ref to data.
class-methods validate_sql_script
changing sqlscript type string.
class-methods validate_table
changing checkedtablename type string
raising lcx_configuration.
methods constructor "Constructore method
importing filefullpath type string
tablename type string
sqlscript type string
raising lcx_configuration.
methods import raising lcx_configuration.
methods export raising lcx_configuration.
protected section.
methods get_filecontent
returning value(filecontent) type xstring
raising lcx_configuration.
methods extract_data_from_table
raising lcx_configuration.
methods check_file
raising lcx_configuration.
methods extract_data_from_excel
raising lcx_configuration.
methods get_tablecontent
exporting tablecontent type any table
raising lcx_configuration.
private section.
endclass.
class lcl_configuration implementation.
method constructor.
if filefullpath is initial or tablename is initial.
raise exception type lcx_configuration
exporting
text = |File Name { filefullpath } and Table Name { tablename } should be provided|.
endif.
me->filefullpath = filefullpath.
me->tablename = tablename.
me->sqlscript = sqlscript.
lcl_configuration=>validate_table( changing checkedtablename = me->tablename ).
me->tablestructure ?= cl_abap_typedescr=>describe_by_name( me->tablename ).
if not me->tablestructure is bound.
raise exception type lcx_configuration
exporting
text = |Exception occurs when parsing Table Structure for { tablename } |.
endif.
try.
me->tabletype = cl_abap_tabledescr=>create( p_line_type = me->tablestructure ).
catch cx_sy_table_creation into data(tabletypeexception).
raise exception type lcx_configuration
exporting
text = |Exception occurs when parsing Table Type for { tablename } |.
endtry.
create data tabledata type handle me->tabletype.
endmethod.
method import.
"Update DDIC table content from (client PC) excel file
me->extract_data_from_excel( ).
field-symbols <finaltabledata> type standard table.
data finaltabledata type ref to data.
create data finaltabledata type handle me->tabletype.
assign finaltabledata->* to <finaltabledata>.
field-symbols <tabledata> type standard table.
assign me->tabledata->* to <tabledata>.
loop at <tabledata> assigning field-symbol(<currenttabledata>).
assign component 'MANDT' of structure <currenttabledata> to field-symbol(<lv_client>).
if sy-subrc = 0.
<lv_client> = ''.
if not <currenttabledata> is initial.
<lv_client> = sy-mandt.
append <currenttabledata> to <finaltabledata>.
else.
"delete <tabledata> from <currenttabledata>.
endif.
else.
if <currenttabledata> is initial.
"delete <tabledata> from <currenttabledata>.
endif.
endif.
endloop.
data(checkedtablename) = me->tablename.
lcl_configuration=>validate_table( changing checkedtablename = checkedtablename ).
if not <finaltabledata> is initial.
modify (checkedtablename) from table <finaltabledata>.
"break-point.
if sy-subrc <> 0.
rollback work.
raise exception type lcx_configuration
exporting
text = |Exception occurs when modifying table: { tablename } |.
else.
message s001(00) with |Table: { tablename } is modified successfully.|.
endif.
endif.
endmethod.
method export.
"Create client PC excel file from DDIC table
data(filecontent) = me->get_filecontent( ).
cl_scp_change_db=>xstr_to_xtab( exporting im_xstring = filecontent
importing ex_xtab = data(filecontenttab) ).
cl_gui_frontend_services=>gui_download(
exporting
bin_filesize = xstrlen( filecontent )
filename = |{ me->filefullpath }|
filetype = 'BIN'
confirm_overwrite = abap_true
importing
filelength = data(bytestransferred)
changing
data_tab = filecontenttab
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 sy-subrc <> 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
else.
message s001(00) with bytestransferred ' bytes transferred'.
endif.
endmethod.
method get_filecontent.
me->extract_data_from_table( ).
if me->tabledata is initial.
raise exception type lcx_configuration
exporting
text = |Table { tablename } has no entry.|.
endif.
"Get file content from table
clear filecontent.
try.
data(xlsx_handling) = cl_ehfnd_xlsx=>get_instance( ).
data(xlsx_document) = xlsx_handling->create_doc( ).
data(xlsx_sheets) = xlsx_document->get_sheets( ).
data(first_xlsx_sheet) = xlsx_document->get_sheet_by_id( xlsx_sheets[ 1 ]-sheet_id ).
first_xlsx_sheet->change_sheet_name( 'Data' ).
data(lv_column) = 1.
loop at me->tablestructure->components reference into data(component).
first_xlsx_sheet->set_cell_content( iv_row = 1 iv_column = lv_column iv_value = component->name ).
lv_column = lv_column + 1.
endloop.
data(lv_row) = 2.
field-symbols <tabledata> type standard table.
assign me->tabledata->* to <tabledata>.
loop at <tabledata> assigning field-symbol(<currenttabledata>).
lv_column = 1.
loop at me->tablestructure->components reference into component.
assign component component->name of structure <currenttabledata> to field-symbol(<columnvalue>).
first_xlsx_sheet->set_cell_content( iv_row = lv_row iv_column = lv_column iv_value = <columnvalue> ).
lv_column = lv_column + 1.
endloop.
lv_row = lv_row + 1.
endloop.
filecontent = xlsx_document->save( ).
catch cx_openxml_format into data(openxml_format_exception).
raise exception type lcx_configuration
exporting
text = |Error occurs when constructing excel file instance. cx_openxml_format|.
catch cx_openxml_not_found into data(openxml_not_found_exception).
raise exception type lcx_configuration
exporting
text = |Error occurs when constructing excel file instance. CX_OPENXML_NOT_FOUND |.
catch cx_openxml_not_allowed into data(openxml_not_allowed_exception).
raise exception type lcx_configuration
exporting
text = |Error occurs when constructing excel file instance. CX_OPENXML_NOT_ALLOWED |.
endtry.
endmethod.
method get_tablecontent.
"Get table content from file
endmethod.
method validate_table.
"raise exception if table does not exist
select single * from tadir into @data(tableinfo) where obj_name = @checkedtablename and object = 'TABL'. "#EC CI_GENBUFF.
if sy-subrc <> 0.
raise exception type lcx_configuration
exporting
text = |Table { checkedtablename } does not exist.|.
endif.
try.
checkedtablename =
cl_abap_dyn_prg=>check_table_or_view_name_str(
val = checkedtablename
packages = conv #( tableinfo-devclass )
incl_sub_packages = abap_true
).
catch cx_abap_not_a_table
cx_abap_not_in_package.
return.
endtry.
endmethod.
method extract_data_from_table.
data sql_script type string.
data checkedtablename type string.
sql_script = me->sqlscript.
checkedtablename = me->tablename.
lcl_configuration=>validate_sql_script( changing sqlscript = sql_script ).
lcl_configuration=>validate_table( changing checkedtablename = checkedtablename ).
field-symbols <tabledata> type standard table.
assign tabledata->* to <tabledata>.
if me->sqlscript is initial.
select * from (checkedtablename) into table <tabledata>.
else.
select * from (checkedtablename) into table <tabledata> where (sql_script).
endif.
endmethod.
method validate_sql_script.
if sqlscript is initial.
return.
endif.
sqlscript = replace( val = sqlscript
sub = `'`
with = `''`
occ = 0 ).
concatenate `'` sqlscript `'` into sqlscript separated by space.
try.
sqlscript =
cl_abap_dyn_prg=>check_char_literal( sqlscript ).
data(lv_len) = strlen( sqlscript ) - 2.
sqlscript = sqlscript+1(lv_len).
sqlscript = replace( val = sqlscript
sub = `''`
with = `'`
occ = 0 ).
catch cx_abap_invalid_value into data(lo_exception).
clear sqlscript.
endtry.
endmethod.
method check_file.
endmethod.
method extract_data_from_excel.
field-symbols <exceldata> type standard table.
assign me->tabledata->* to <exceldata>.
data(xlsxhandler) = cl_ehfnd_xlsx=>get_instance( ).
check not xlsxhandler is initial.
try.
data(xstring_excel) = cl_openxml_helper=>load_local_file( me->filefullpath ).
catch cx_openxml_not_found into data(openxml_not_found).
return.
endtry.
try.
data(xlsxdocument) = xlsxhandler->load_doc( iv_file_data = xstring_excel ).
catch cx_openxml_format into data(openxml_format).
return.
catch cx_openxml_not_allowed into data(openxml_not_allowed).
return.
catch cx_dynamic_check into data(dynamic_check).
return.
endtry.
"extract data from first sheet
try.
data(firstsheet) = xlsxdocument->get_sheet_by_id( iv_sheet_id = 1 ).
catch cx_openxml_format into openxml_format.
raise exception type lcx_configuration
exporting
text = |Error occurs when extract data from first sheet: CX_OPENXML_FORMAT |.
catch cx_openxml_not_found into openxml_not_found.
raise exception type lcx_configuration
exporting
text = |Error occurs when extract data from first sheet: OPENXML_NOT_FOUND |.
catch cx_dynamic_check into dynamic_check.
raise exception type lcx_configuration
exporting
text = |Error occurs when extract data from first sheet: CX_DYNAMIC_CHECK |.
endtry.
"return if no sheet in xlsx file
check not firstsheet is initial.
"check file structure, first line of excel file
data(columncount) = firstsheet->get_last_column_number_in_row( 1 ).
data column type i value 1.
"data tablecomponents type cl_abap_structdescr=>component_table.
data(tablecomponents) = me->tablestructure->get_components( ).
data invalidcolumn type string.
types: begin of columninfo,
column type i,
columnname type string,
end of columninfo.
types columnsinfo type standard table of columninfo with empty key.
data columnfromfile type columnsinfo.
do columncount times.
data(cellvalue) = firstsheet->get_cell_content(
exporting
iv_row = 1
iv_column = column ).
append initial line to columnfromfile assigning field-symbol(<columnfromfile>).
<columnfromfile>-column = column.
<columnfromfile>-columnname = cellvalue.
if line_exists( tablecomponents[ name = cellvalue ] ).
delete tablecomponents where name = cellvalue.
else.
invalidcolumn = invalidcolumn && |,{ cellvalue }|.
endif.
column = column + 1.
enddo.
data missingcolumns type string.
loop at tablecomponents reference into data(currentcomponent).
missingcolumns = missingcolumns && |, { currentcomponent->*-name }|.
endloop.
if not invalidcolumn is initial.
raise exception type lcx_configuration
exporting
text = |Find invalid columns: { invalidcolumn } |.
endif.
if not missingcolumns is initial.
raise exception type lcx_configuration
exporting
text = |Columns do not exist in excel file: { missingcolumns } |.
endif.
tablecomponents = me->tablestructure->get_components( ).
data(rowcount) = firstsheet->get_last_row_number( ).
data currentrow type i value 2.
while currentrow <= rowcount.
append initial line to <exceldata> assigning field-symbol(<currentrow>).
loop at columnfromfile reference into data(currentcolumn).
cellvalue = firstsheet->get_cell_content(
exporting
iv_row = currentrow
iv_column = currentcolumn->*-column ).
assign component currentcolumn->*-columnname of structure <currentrow> to field-symbol(<cellvalue>).
<cellvalue> = cellvalue.
endloop.
currentrow = currentrow + 1.
endwhile.
endmethod.
endclass.
Conclusion
Thank you for your interest in XLSX2ABAP and ABAP2XLSX. Example code is tested on SAP_BASIS 755, S4CORE 105.
Skip to content Skip to blog sidebar
ABAP code to upload XLSX file to SAP using ABAP. The method shown in the blog will work in SAP GUI and Webgui (program launched from Fiori Launchpad). This approach will also work with OData.
Code is based on class CL_FDT_XL_SPREADSHEET which can be instantiated using the file name and excel file in XSTRING variable. This processing of file as XSTRING is particularly useful in case of Fiori Apps, OData CREATE_STREAM method pass uploaded file already in XSTRING which can be simply passed on to class CL_FDT_XL_SPREADSHEET constructor.
After instantiating the class get list of worksheets in the file using IF_FDT_DOC_SPREADSHEET~GET_WORKSHEET_NAMES. Method GET_WORKSHEET_NAMES returns internal table. In most of the case, you would read the first worksheet name and call method IF_FDT_DOC_SPREADSHEET~GET_ITAB_FROM_WORKSHEET with worksheet name to get data in a dynamic internal table. However, if you have data over multiple worksheets then you would have to call method GET_ITAB_FROM_WORKSHEET in the loop for each worksheet.
There are some function modules (TEXT_CONVERT_XLS_TO_SAP) available in SAP which can help you read data from XLS/XLSX file but these are based on Office Integration and requires MS Office installed on your PC. Also, this Office Integration solution (along with a range of other features) is not supported in WebGUI. In other words, these FMs will only work when you are running your program in SAP WebGUI.
I have structured the logic in standalone SE38 program so you can copy-paste and have it ready for testing in your system. Obviously, you would have to adapt the code to use it in your application but you get the idea of how it works.
*---------------------------------------------------------------------* * Report ZPW_EXCELUPLOAD *---------------------------------------------------------------------* REPORT zpw_excelupload. FIELD-SYMBOLS : <gt_data> TYPE STANDARD TABLE . SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME . PARAMETERS : p_file TYPE ibipparms-path OBLIGATORY, p_ncol TYPE i OBLIGATORY DEFAULT 10. SELECTION-SCREEN END OF BLOCK b1 . *--------------------------------------------------------------------* * at selection screen *--------------------------------------------------------------------* AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. DATA: lv_rc TYPE i. DATA: lt_file_table TYPE filetable, ls_file_table TYPE file_table. CALL METHOD cl_gui_frontend_services=>file_open_dialog EXPORTING window_title = 'Select a file' CHANGING file_table = lt_file_table rc = lv_rc. IF sy-subrc = 0. READ TABLE lt_file_table INTO ls_file_table INDEX 1. p_file = ls_file_table-filename. ENDIF. START-OF-SELECTION . PERFORM read_file . PERFORM process_file. *---------------------------------------------------------------------* * Form READ_FILE *---------------------------------------------------------------------* FORM read_file . DATA : lv_filename TYPE string, lt_records TYPE solix_tab, lv_headerxstring TYPE xstring, lv_filelength TYPE i. lv_filename = p_file. CALL FUNCTION 'GUI_UPLOAD' EXPORTING filename = lv_filename filetype = 'BIN' IMPORTING filelength = lv_filelength header = lv_headerxstring TABLES data_tab = lt_records 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. "convert binary data to xstring "if you are using cl_fdt_xl_spreadsheet in odata then skips this step "as excel file will already be in xstring CALL FUNCTION 'SCMS_BINARY_TO_XSTRING' EXPORTING input_length = lv_filelength IMPORTING buffer = lv_headerxstring TABLES binary_tab = lt_records EXCEPTIONS failed = 1 OTHERS = 2. IF sy-subrc <> 0. "Implement suitable error handling here ENDIF. DATA : lo_excel_ref TYPE REF TO cl_fdt_xl_spreadsheet . TRY . lo_excel_ref = NEW cl_fdt_xl_spreadsheet( document_name = lv_filename xdocument = lv_headerxstring ) . CATCH cx_fdt_excel_core. "Implement suitable error handling here ENDTRY . "Get List of Worksheets lo_excel_ref->if_fdt_doc_spreadsheet~get_worksheet_names( IMPORTING worksheet_names = DATA(lt_worksheets) ). IF NOT lt_worksheets IS INITIAL. READ TABLE lt_worksheets INTO DATA(lv_woksheetname) INDEX 1. DATA(lo_data_ref) = lo_excel_ref->if_fdt_doc_spreadsheet~get_itab_from_worksheet( lv_woksheetname ). "now you have excel work sheet data in dyanmic internal table ASSIGN lo_data_ref->* TO <gt_data>. ENDIF. ENDFORM. *---------------------------------------------------------------------* * Form PROCESS_FILE *---------------------------------------------------------------------* FORM process_file . DATA : lv_numberofcolumns TYPE i, lv_date_string TYPE string, lv_target_date_field TYPE datum. FIELD-SYMBOLS : <ls_data> TYPE any, <lv_field> TYPE any. "you could find out number of columns dynamically from table <gt_data> lv_numberofcolumns = p_ncol . LOOP AT <gt_data> ASSIGNING <ls_data> FROM 2 . "processing columns DO lv_numberofcolumns TIMES. ASSIGN COMPONENT sy-index OF STRUCTURE <ls_data> TO <lv_field> . IF sy-subrc = 0 . CASE sy-index . * when 1 . * when 2 . WHEN 10 . lv_date_string = <lv_field> . PERFORM date_convert USING lv_date_string CHANGING lv_target_date_field . WRITE lv_target_date_field . WHEN OTHERS. WRITE : <lv_field> . ENDCASE . ENDIF. ENDDO . NEW-LINE . ENDLOOP . ENDFORM. *---------------------------------------------------------------------* * Form DATE_CONVERT *---------------------------------------------------------------------* FORM date_convert USING iv_date_string TYPE string CHANGING cv_date TYPE datum . DATA: lv_convert_date(10) TYPE c. lv_convert_date = iv_date_string . "date format YYYY/MM/DD FIND REGEX '^d{4}[/|-]d{1,2}[/|-]d{1,2}$' IN lv_convert_date. IF sy-subrc = 0. CALL FUNCTION '/SAPDMC/LSM_DATE_CONVERT' EXPORTING date_in = lv_convert_date date_format_in = 'DYMD' to_output_format = ' ' to_internal_format = 'X' IMPORTING date_out = lv_convert_date EXCEPTIONS illegal_date = 1 illegal_date_format = 2 no_user_date_format = 3 OTHERS = 4. ELSE. " date format DD/MM/YYYY FIND REGEX '^d{1,2}[/|-]d{1,2}[/|-]d{4}$' IN lv_convert_date. IF sy-subrc = 0. CALL FUNCTION '/SAPDMC/LSM_DATE_CONVERT' EXPORTING date_in = lv_convert_date date_format_in = 'DDMY' to_output_format = ' ' to_internal_format = 'X' IMPORTING date_out = lv_convert_date EXCEPTIONS illegal_date = 1 illegal_date_format = 2 no_user_date_format = 3 OTHERS = 4. ENDIF. ENDIF. IF sy-subrc = 0. cv_date = lv_convert_date . ENDIF. ENDFORM .
Uploaded this excel file.
Below results from SAP GUI and WebGUI.
ABAP программа загрузки данных из EXCEL в SAP систему. Разберу на примере загрузку Excel файла, проверку и преобразование данных, полученных при загрузке ABAP.
Это полезная и часто используемая задача.
Рассмотрим один из вариантов загрузки данных из Excel на примере программы ABAP. Подробно разберу каждый блок программы для лучшего понимания. Ссылку на файл Excel для тестирования данной программы приведу в конце данной статьи.
Селекционный экран ABAP для выбора файла
Создаю селекционный экран ABAP на который добавлю одно поле для выбора файла из которого будут загружаться данные.
PARAMETERS p_file TYPE string LOWER CASE. |
Средство поиска ABAP для файла
Так же в этой части кода добавляю средство поиска ABAP для поля файла через использование события AT SELECTION-SCREEN ON VALUE-REQUEST.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. cl_salv_test_data=>select_file( IMPORTING filename = p_file ). |
Определяю тип для внутренней таблицы результата
Я определил локальный тип структуры результата для большей наглядности в статье, но на его месте может быть и тип ABAP словаря.
START-OF-SELECTION. TYPES: BEGIN OF ts_data, carrid TYPE s_carr_id, connid TYPE s_conn_id, fldate TYPE s_date, price TYPE s_price, currency TYPE s_currcode, planetype TYPE s_planetye, seatsmax TYPE s_seatsmax, seatsocc TYPE s_seatsocc, gjahr TYPE gjahr, monat TYPE monat, END OF ts_data. |
Внутренняя таблица lt_sflight это таблица в которую будет помещён итоговый уже преобразованный результат загрузки. А таблица lt_intern это внутренняя таблица abap в которую изначально будут помещены данные из Excel для дальнейшей обработки.
DATA: lt_sflight TYPE STANDARD TABLE OF ts_data, lt_intern TYPE TABLE OF alsmex_tabline. |
ФМ ALSM_EXCEL_TO_INTERNAL_TABLE
Сама загрузка происходит при вызове функционального метода ALSM_EXCEL_TO_INTERNAL_TABLE, разберём подробно его параметры:
CALL FUNCTION ‘ALSM_EXCEL_TO_INTERNAL_TABLE’ EXPORTING filename = CONV rlgrap—filename( p_file ) i_begin_col = 1 i_begin_row = 2 i_end_col = 10 i_end_row = 10000 TABLES intern = lt_intern EXCEPTIONS inconsistent_parameters = 1 upload_ole = 2 OTHERS = 3. |
- filename – путь к файлу Excel. Использование конструкции CONV в этом случае преобразует параметр p_file к нужному типу.
- i_begin_col – номер колонки с которой начинать загрузку
- i_begin_row – номер строки с которой начинаем загрузку. В нашем случае установил значение 2 так как мне не нужно обрабатывать первую строку заголовка.
- i_end_col – сколько колонок нам нужно анализировать при загрузке
- i_end_row – сколько записей максимум берётся для анализа.
- intern – внутренняя таблица в которую будут загружаться данные из файла.
Обработка результата загрузки данных из Excel
С использованием конструкции нового синтаксиса LOOP AT GROUP BY обходим таблицу сгруппировав её по столбцу row, в котором расположен номер строки. И после обработки всей группы с одинаковым номером строки мы добавляем запись в таблицу результата и переходим к следующей группе.
Более подробно загрузка и обработка данных после разбирается в видео и не забывайте подписаться на мой канал YouCoder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
LOOP AT lt_intern INTO DATA(ls_intern) GROUP BY ( row = ls_intern—row ) REFERENCE INTO DATA(lr_grp). DATA(ls_alv) = VALUE ts_data( ). LOOP AT GROUP lr_grp REFERENCE INTO DATA(lr_item). CHECK lr_item—>value IS NOT INITIAL. CASE lr_item—>col. WHEN 1. ls_alv—carrid = lr_item—>value. WHEN 2. ls_alv—connid = lr_item—>value. WHEN 3. ls_alv—fldate = |{ lr_item—>value+6(4) }{ lr_item—>value+3(2) }{ lr_item—>value(2) }|. » 29.04.2015 20150429 WHEN 4. lr_item—>value = replace( val = lr_item—>value regex =‘[,]’ with = ‘.’ occ = 0 ). » 427,94 ls_alv—price = lr_item—>value. WHEN 5. ls_alv—currency = lr_item—>value. WHEN 6. ls_alv—planetype = lr_item—>value. WHEN 7. ls_alv—seatsmax = lr_item—>value. WHEN 8. IF strlen( lr_item—>value ) > 10. ls_alv—seatsocc = lr_item—>value. ENDIF. WHEN 9. IF lr_item—>value CO |0123456789| AND lr_item—>value >= 1 AND lr_item—>value <= 9999. ls_alv—gjahr = lr_item—>value. ENDIF. WHEN 10. IF lr_item—>value CO |0123456789| AND lr_item—>value >= 1 AND lr_item—>value <= 12 . ls_alv—monat = lr_item—>value. ENDIF. WHEN OTHERS. CONTINUE. ENDCASE. ENDLOOP. APPEND ls_alv TO lt_sflight. ENDLOOP. |
Конструкции языка ABAP используемые в коде выше:
- replace – замена символа в строке ABAP
- strlen – длина строки ABAP
- LOOP AT GROUP – группировка в цикле ABAP
Быстрый вывод внутренней таблицы ABAP на экран для тестирования:
cl_demo_output=>display( lt_sflight ). |
Шаблон программы ABAP загрузки данных из Excel.
Ниже привёл полный код программы загрузки для удобства копирования и тестирования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
START-OF-SELECTION. TYPES: BEGIN OF ts_data, carrid TYPE s_carr_id, connid TYPE s_conn_id, fldate TYPE s_date, price TYPE s_price, currency TYPE s_currcode, planetype TYPE s_planetye, seatsmax TYPE s_seatsmax, seatsocc TYPE s_seatsocc, gjahr TYPE gjahr, monat TYPE monat, END OF ts_data. DATA: lt_sflight TYPE STANDARD TABLE OF ts_data, lt_intern TYPE TABLE OF alsmex_tabline. IF p_file IS NOT INITIAL. CALL FUNCTION ‘ALSM_EXCEL_TO_INTERNAL_TABLE’ EXPORTING filename = CONV rlgrap—filename( p_file ) i_begin_col = 1 i_begin_row = 2 i_end_col = 10 i_end_row = 10000 TABLES intern = lt_intern EXCEPTIONS inconsistent_parameters = 1 upload_ole = 2 OTHERS = 3. IF sy—subrc = 0. LOOP AT lt_intern INTO DATA(ls_intern) GROUP BY ( row = ls_intern—row ) REFERENCE INTO DATA(lr_grp). DATA(ls_alv) = VALUE ts_data( ). LOOP AT GROUP lr_grp REFERENCE INTO DATA(lr_item). CHECK lr_item—>value IS NOT INITIAL. CASE lr_item—>col. WHEN 1. ls_alv—carrid = lr_item—>value. WHEN 2. ls_alv—connid = lr_item—>value. WHEN 3. ls_alv—fldate = |{ lr_item—>value+6(4) }{ lr_item—>value+3(2) }{ lr_item—>value(2) }|. » 29.04.2015 20150429 WHEN 4. lr_item—>value = replace( val = lr_item—>value regex =‘[,]’ with = ‘.’ occ = 0 ). » 427,94 ls_alv—price = lr_item—>value. WHEN 5. ls_alv—currency = lr_item—>value. WHEN 6. ls_alv—planetype = lr_item—>value. WHEN 7. ls_alv—seatsmax = lr_item—>value. WHEN 8. IF strlen( lr_item—>value ) > 10. ls_alv—seatsocc = lr_item—>value. ENDIF. WHEN 9. IF lr_item—>value CO |0123456789| AND lr_item—>value >= 1 AND lr_item—>value <= 9999. ls_alv—gjahr = lr_item—>value. ENDIF. WHEN 10. IF lr_item—>value CO |0123456789| AND lr_item—>value >= 1 AND lr_item—>value <= 12 . ls_alv—monat = lr_item—>value. ENDIF. WHEN OTHERS. CONTINUE. ENDCASE. ENDLOOP. APPEND ls_alv TO lt_sflight. ENDLOOP. ENDIF. cl_demo_output=>display( lt_sflight ). ENDIF. |
Если статья оказалась Вам полезна поддержите проект. Спасибо!
Сайт ABAP программирование YouCoder – это сборник статей и видео о языке программирования ABAP и работе в SAP системе. Есть как уроки abap для начинающих так и новый синтаксис в ABAP и SAP обучение для консультантов SAP.
Upload Excel File to internal table in SAP using the standard FM TEXT_CONVERT_XLS_TO_SAP is a common business requirement for fast upload data or mass data manipulation.
Find also a full ABAP Program, just copy and paste it and start playing with Excel.
The simplest way is to use standard function TEXT_CONVERT_XLS_TO_SAP
- i_line_header: defines if the file has a header line (column for title)
- i_filename : set the filename
- i_tab_converted_data : is the internal table containing data from Excel file.
SAP Upload Excel File to ABAP Internal Table
Here a sample ABAP Program to upload Excel file to internal table in sap. It will take an Excel file as input and import the content to an ABAP internal table.
The step-step guide is :
- Call the Function F4_FILENAME: This function will help select the Excel file from your local driver.
- Call to the magical standard SAP TEXT_CONVERT_XLS_TO_SAP.
Look above for the Function TEXT_CONVERT_XLS_TO_SAP signature and description.
These function will return the data in Excel into SAP internal table. - Display Excel content with a sample Write Structure.
Do whatever you want with the data.
ABAP Report to Upload Excel File in SAP
Here a sample ABAP Report ready to be copied and which, hopefully, will help you as a skeleton for your Excel File processing in SAP.
In fact, this ABAP code will
- upload an Excel File for Local (using the F4_FILENAME to select the Excel file)
- and transfer all the Excel data to ABAP internal Table using TEXT_CONVERT_XLS_TO_SAP (to be displayed later).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
TYPE—POOLS: truxs. PARAMETERS: p_file TYPE rlgrap—filename. PARAMETERS: p_head TYPE char01 DEFAULT ‘X’. TYPES: BEGIN OF t_datatab, col1(30) TYPE c, col2(30) TYPE c, col3(30) TYPE c, END OF t_datatab. DATA: it_datatab TYPE STANDARD TABLE OF t_datatab, wa_datatab TYPE t_datatab. DATA: it_raw TYPE truxs_t_text_data. * At selection screen AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. CALL FUNCTION ‘F4_FILENAME’ EXPORTING field_name = ‘P_FILE’ IMPORTING file_name = p_file. START-OF-SELECTION. » Convert Excel Data to SAP internal Table Data CALL FUNCTION ‘TEXT_CONVERT_XLS_TO_SAP’ EXPORTING * I_FIELD_SEPERATOR = i_line_header = p_head i_tab_raw_data = it_raw » WORK TABLE i_filename = p_file TABLES i_tab_converted_data = it_datatab[] «ACTUAL DATA EXCEPTIONS conversion_failed = 1 OTHERS = 2. IF sy—subrc <> 0. MESSAGE ID sy—msgid TYPE sy—msgty NUMBER sy—msgno WITH sy—msgv1 sy—msgv2 sy—msgv3 sy—msgv4. ENDIF. *********************************************************************** * END-OF-SELECTION. END-OF-SELECTION. » For sample, Excel Data transfered to internal table is displayed with write LOOP AT it_datatab INTO wa_datatab. WRITE:/ wa_datatab—col1, wa_datatab—col2, wa_datatab—col3. ENDLOOP. |
Managing Excel file with ABAP: ABAP2XLSX
If you want to go further with Excel in SAP, have a look on the SAPLINK ABAP2XLSX. It is available in GitHub.
I have already used it with a productive project, and I was surprised with how many opportunities it gives.
For example, you can manage very easily the formatting of Cell with the ABAP2XLSX classes.
Handling Data to and from Excel, is a way more completed and easily using the predefined methods.
Preface – This post is part of the ABAP Programs series.
To perform ABAP excel upload using ABAP report, we need to call a FM ‘TEXT_CONVERT_XLS_TO_SAP’. This FM converts the excel data to ABAP data. Now we can store the same in our internal table and later in our database table. The only mandatory requirement is that the table fields should match the header of excel in a synchronous way, else wrong data will be saved.
Introduction
Many times we need to upload an excel data from our local storage on PC and insert those data into our ABAP tables. This can be easily achieved using ABAP Function Module ‘TEXT_CONVERT_XLS_TO_SAP’. In the given program we convert our excel data into ABAP data and insert those data in our ztable.
ABAP Excel Upload – Image Illustration of process
ABAP Program
TYPE-POOLS truxs. PARAMETERS p_file TYPE rlgrap-filename. TYPES : BEGIN OF t_tab, filename TYPE char100sm, END OF t_tab. DATA : t_upload TYPE STANDARD TABLE OF t_tab, wa_upload TYPE t_tab, it_type TYPE truxs_t_text_data. AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. CALL FUNCTION 'F4_FILENAME' EXPORTING field_name = 'p_file' IMPORTING file_name = p_file. START-OF-SELECTION. CALL FUNCTION 'TEXT_CONVERT_XLS_TO_SAP' EXPORTING i_tab_raw_data = it_type i_filename = p_file TABLES i_tab_converted_data = t_upload[] EXCEPTIONS conversion_failed = 1 OTHERS = 2. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 . ENDIF. END-OF-SELECTION. DATA : ls_cc_impl TYPE zBarry_upload, lt_cc_impl TYPE STANDARD TABLE OF zBarry_upload. LOOP AT t_upload INTO wa_upload. ls_cc_impl-mandt = sy-mandt. ls_cc_impl-filename = wa_upload-filename. APPEND ls_cc_impl TO lt_cc_impl. ENDLOOP. MODIFY zBarry_upload FROM TABLE lt_cc_impl.
Table and Excel
Excel Data for Upload in ABAP Table
Upload Excel Data
Explanation
In the above program, we have implemented the following steps:
- Initially, we have defined TYPE-POOLS truxs. TYPE-POOLS have predefined methods that can be reused in multiple programs. Here we are using truxs, that is used later as truxs_t_text_data, a data type.
- Now, we have define parameters: p_file. This will be used to take the file location as input.
- Later, we have defined variables: t_tab which is a table type, t_upload, wa_upload and it_type.
- Now, we have called a function module “F4_FILENAME” to process the file upload.
- Then, we have called another function module ‘TEXT_CONVERT_XLS_TO_SAP’ to get the ABAP form of data into an internal table.
- Once we have our data in an internal table, then we can easily append it to our data base table using loop.