A maioria dos programadores ABAP com alguma experiência conhece os métodos para fazer upload ou download de ficheiros. Mas nem todos os usam de uma forma prática. Neste artigo vou descrever a forma mais simples para usar estes métodos e uma forma mais complexa que permite o tratamento de ficheiros de comprimento de linha variável.
Mas primeiro, uma parte importante, a chamada à pesquisa do nome do ficheiro.
Pesquisa de ficheiro
A não ser que o ficheiro tenha um nome e localização específica, a pesquisa de ficheiro deve ser obrigatória. Não preparar a pesquisa é um erro grotesco que limita a operacionalidade e usabilidade do programa. Um programador deve sempre simplificar o trabalho do utilizador final e isso implica primeiro que tudo que o programador se pergunte como se faz no SAP standard. Ou melhor, o bom standard…
Portanto, normalmente o nome do ficheiro é pedido por parâmetro e a pesquisa é efectuada por F4.
*Primeira consideração, o parâmetro deve ter cumprimento suficiente.
*No exemplo, o parâmetro tem um comprimento de 1024 caracteres,
*mostrando apenas 30. O tamanho máximo do nome do ficheiro varia
*consoante o formato do file system, mas por motivos de simplificação
*vamos considerar 1024 suficiente. Não é nada espectável um nome mais
*longo logo o esforço de tratamento é desnecessário.
PARAMETERS: p_file LIKE file_table-filename LENGTH 30.
*A chamada F4 é tratada no evento ON VALUE-REQUEST.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
PERFORM p_file_f4.
*&---------------------------------------------------------------------*
*& Form p_file_f4
*&---------------------------------------------------------------------*
* Diálogo de Pesquisa de Ficheiro
*----------------------------------------------------------------------*
FORM p_file_f4.
DATA: l_title TYPE string,
l_rc TYPE i.
DATA: lt_filetable TYPE filetable.
FIELD-SYMBOLS: <fs_line> TYPE file_table.
l_title = 'Ficheiro'.
CLEAR lt_filetable[].
CALL METHOD cl_gui_frontend_services=>file_open_dialog
EXPORTING
window_title = l_title
CHANGING
file_table = lt_filetable
rc = l_rc
EXCEPTIONS
file_open_dialog_failed = 1
cntl_error = 2
error_no_gui = 3
not_supported_by_gui = 4
OTHERS = 5.
*não é previsto que caso o código esteja correcto haja erros no
*diálogo, logo não os tratei.
*Pelo motivo indicado em cima, o nome do ficheiro deve estar todo na
*primeira linha.
*No exemplo, o parâmetro tem um comprimento de 1024 caracteres,
*mostrando apenas 30. O tamanho máximo do nome do ficheiro varia
*consoante o formato do file system, mas por motivos de simplificação
*vamos considerar 1024 suficiente. Não é nada espectável um nome mais
*longo logo o esforço de tratamento é desnecessário.
PARAMETERS: p_file LIKE file_table-filename LENGTH 30.
*A chamada F4 é tratada no evento ON VALUE-REQUEST.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
PERFORM p_file_f4.
*&---------------------------------------------------------------------*
*& Form p_file_f4
*&---------------------------------------------------------------------*
* Diálogo de Pesquisa de Ficheiro
*----------------------------------------------------------------------*
FORM p_file_f4.
DATA: l_title TYPE string,
l_rc TYPE i.
DATA: lt_filetable TYPE filetable.
FIELD-SYMBOLS: <fs_line> TYPE file_table.
l_title = 'Ficheiro'.
CLEAR lt_filetable[].
CALL METHOD cl_gui_frontend_services=>file_open_dialog
EXPORTING
window_title = l_title
CHANGING
file_table = lt_filetable
rc = l_rc
EXCEPTIONS
file_open_dialog_failed = 1
cntl_error = 2
error_no_gui = 3
not_supported_by_gui = 4
OTHERS = 5.
*não é previsto que caso o código esteja correcto haja erros no
*diálogo, logo não os tratei.
*Pelo motivo indicado em cima, o nome do ficheiro deve estar todo na
*primeira linha.
LOOP AT lt_filetable ASSIGNING <fs_line>.
IF sy-tabix = 2.
*mensagem de erro para limitar o nome do ficheiro
MESSAGE i899(mm) WITH 'Nome de ficheiro demasiado' 'comprido'.
EXIT.
ENDIF.
p_file = <fs_line>-filename.
ENDLOOP.
ENDFORM. "p_file_f4
IF sy-tabix = 2.
*mensagem de erro para limitar o nome do ficheiro
MESSAGE i899(mm) WITH 'Nome de ficheiro demasiado' 'comprido'.
EXIT.
ENDIF.
p_file = <fs_line>-filename.
ENDLOOP.
ENDFORM. "p_file_f4
O exemplo em cima é com o diálogo para abertura de ficheiro. O diálogo para gravar ficheiro é o cl_gui_frontend_services=>file_save_dialog e é ligeiramente diferente, fica aqui um exemplo de como usar:
DATA: l_file TYPE string, l_path TYPE string, l_fullpath TYPE string.
CALL METHOD cl_gui_frontend_services=>file_save_dialog
CHANGING
filename = l_file
path = l_path
fullpath = l_fullpath
EXCEPTIONS
cntl_error = 1
error_no_gui = 2
not_supported_by_gui = 3
OTHERS = 4.
*também se poderia controlar o comprimento do nome do ficheiro neste ponto
p_file = l_fullpath.
CALL METHOD cl_gui_frontend_services=>file_save_dialog
CHANGING
filename = l_file
path = l_path
fullpath = l_fullpath
EXCEPTIONS
cntl_error = 1
error_no_gui = 2
not_supported_by_gui = 3
OTHERS = 4.
*também se poderia controlar o comprimento do nome do ficheiro neste ponto
p_file = l_fullpath.
Formato simplificado de Upload/Download
Os métodos de upload e download são os bem conhecidos:
- cl_gui_frontend_services=>gui_upload
- cl_gui_frontend_services=>gui_download
Ambos funcionam basicamente da mesma forma.
A forma simples de chamar a função depende apenas do tipo de campos a passar. Se todos os campos tiverem um formato equiparado a texto então basta usar a tabela interna directamente. Senão, será necessário preparar uma segunda tabela interna com apenas campos equiparados a texto.
Neste exemplo todas as entradas da tabela de centros estão na tabela interna it_t001w:
DATA: it_t001w TYPE TABLE OF t001w.
O conteúdo da tabela é colocado num ficheiro no frontend:
DATA: l_file TYPE string.
l_file = p_file.
CALL METHOD cl_gui_frontend_services=>gui_download
EXPORTING
filename = l_file
write_field_separator = 'X'
CHANGING
data_tab = it_t001w
EXCEPTIONS
* .....
OTHERS = 24.
IF sy-subrc <> 0.
MESSAGE e899(mm) WITH 'Erro a gravar ficheiro'.
ENDIF.
l_file = p_file.
CALL METHOD cl_gui_frontend_services=>gui_download
EXPORTING
filename = l_file
write_field_separator = 'X'
CHANGING
data_tab = it_t001w
EXCEPTIONS
* .....
OTHERS = 24.
IF sy-subrc <> 0.
MESSAGE e899(mm) WITH 'Erro a gravar ficheiro'.
ENDIF.
O tratamento de erros deste método é muito mais relevante dado que a probabilidade do erro é maior.
O parâmetro write_field_separator associa o separador TAB entre campos o que vai permitir abrir o ficheiro facilmente em Excel como um ficheiro *.txt tab delimited, logo é um formato preferencial.
Para este exemplo o upload teria o seguinte aspecto:
DATA: l_file TYPE string.
l_file = p_file.
CALL METHOD cl_gui_frontend_services=>gui_upload
EXPORTING
filename = l_file
has_field_separator = 'X'
CHANGING
data_tab = it_t001w
EXCEPTIONS
* .....
OTHERS = 19.
IF sy-subrc <> 0.
MESSAGE e899(mm) WITH 'Erro a abrir ficheiro'.
ENDIF.
l_file = p_file.
CALL METHOD cl_gui_frontend_services=>gui_upload
EXPORTING
filename = l_file
has_field_separator = 'X'
CHANGING
data_tab = it_t001w
EXCEPTIONS
* .....
OTHERS = 19.
IF sy-subrc <> 0.
MESSAGE e899(mm) WITH 'Erro a abrir ficheiro'.
ENDIF.
Portanto, razoavelmente simples, não é? Ok, vamos complicar. E se à partida temos um ficheiro com colunas dinâmicas?
Upload/Download com estruturas variáveis
Existem formas de criar tabelas internas com colunas dinâmicas mas estes métodos estão fora do âmbito deste artigo. Vamos admitir que a tabela pode ter até 59 colunas, que podem ser campos de texto, datas ou numéricos.
Um ponto extremamente importante diz respeito à conversão entre formato interno e externo. Caso existam campos sujeitos a conversão como códigos de material, datas, números, o programador deve fazer uma escolha consciente do formato do ficheiro. Por regra, o ficheiro que está fora de SAP deve usar o formato de output, ao passo que obviamente o programa e a sua tabela interna devem usar o formato de input. No entanto pode haver excepções dado que o ficheiro provavelmente será processado por uma aplicação externa, por exemplo, o Excel. Aí, considerações como o caracter da casa decimal têm de ser avaliadas. O tratamento de formatos internos e externos também não é do âmbito deste documento, mas serão usados alguns casos a título de exemplo.
Dado que não sabemos o formato, a tabela interna usada tem todos os campos como texto de 30 caracteres:
TYPES: BEGIN OF t_data,
col1(30),
col2(30),
col3(30),
*..............
col59(30),
END OF t_data.
DATA: l_file TYPE string.
DATA: lt_data TYPE TABLE OF t_data.
DATA: ls_data TYPE t_data.
DATA: l_col TYPE sy-tabix.
FIELD-SYMBOLS: <fs_field_in>, <fs_field_out>.
FIELD-SYMBOLS: <fs_t001w> TYPE t001w.
TYPES: BEGIN OF t_line,
fieldname TYPE dd03l-fieldname,
position TYPE dd03l-position,
END OF t_line.
DATA: lt_line TYPE TABLE OF t_line.
FIELD-SYMBOLS <fs_line> TYPE t_line.
l_file = p_file.
*obtem lista de campos para exemplo
*num caso real teríamos por exemplo uma lista reduzida com 4 ou 5 campos
SELECT fieldname position INTO TABLE lt_line FROM dd03l
WHERE tabname = 'T001W' AND comptype = 'E'
ORDER BY position.
LOOP AT it_t001w ASSIGNING <fs_t001w>. "linha a linha
CLEAR ls_data.
LOOP AT lt_line ASSIGNING <fs_line>. "coluna a coluna
ADD 1 TO l_col.
ASSIGN COMPONENT <fs_line>-fieldname OF STRUCTURE <fs_t001w>
TO <fs_field_in>. "assigna campo in
ASSIGN COMPONENT l_col OF STRUCTURE ls_data
TO <fs_field_out>. "assigna campo out
*rotinas de conversão. Existem formas mais dinamicas de as determinar,
*pelo elemento de dados
IF <fs_line>-fieldname = 'KUNNR' OR <fs_line>-fieldname = 'LIFNR'.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
input = <fs_field_in>
IMPORTING
output = <fs_field_out>.
ELSE. "outros elementos não sujeitos a conversão
<fs_field_out> = <fs_field_in>.
ENDIF.
ENDLOOP.
CLEAR l_col.
APPEND ls_data TO lt_data.
ENDLOOP.
Após o carregamento da tabela de output, os dados são enviados sem mais tratamento:
col1(30),
col2(30),
col3(30),
*..............
col59(30),
END OF t_data.
DATA: l_file TYPE string.
DATA: lt_data TYPE TABLE OF t_data.
DATA: ls_data TYPE t_data.
DATA: l_col TYPE sy-tabix.
FIELD-SYMBOLS: <fs_field_in>, <fs_field_out>.
FIELD-SYMBOLS: <fs_t001w> TYPE t001w.
TYPES: BEGIN OF t_line,
fieldname TYPE dd03l-fieldname,
position TYPE dd03l-position,
END OF t_line.
DATA: lt_line TYPE TABLE OF t_line.
FIELD-SYMBOLS <fs_line> TYPE t_line.
l_file = p_file.
*obtem lista de campos para exemplo
*num caso real teríamos por exemplo uma lista reduzida com 4 ou 5 campos
SELECT fieldname position INTO TABLE lt_line FROM dd03l
WHERE tabname = 'T001W' AND comptype = 'E'
ORDER BY position.
LOOP AT it_t001w ASSIGNING <fs_t001w>. "linha a linha
CLEAR ls_data.
LOOP AT lt_line ASSIGNING <fs_line>. "coluna a coluna
ADD 1 TO l_col.
ASSIGN COMPONENT <fs_line>-fieldname OF STRUCTURE <fs_t001w>
TO <fs_field_in>. "assigna campo in
ASSIGN COMPONENT l_col OF STRUCTURE ls_data
TO <fs_field_out>. "assigna campo out
*rotinas de conversão. Existem formas mais dinamicas de as determinar,
*pelo elemento de dados
IF <fs_line>-fieldname = 'KUNNR' OR <fs_line>-fieldname = 'LIFNR'.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
input = <fs_field_in>
IMPORTING
output = <fs_field_out>.
ELSE. "outros elementos não sujeitos a conversão
<fs_field_out> = <fs_field_in>.
ENDIF.
ENDLOOP.
CLEAR l_col.
APPEND ls_data TO lt_data.
ENDLOOP.
Após o carregamento da tabela de output, os dados são enviados sem mais tratamento:
CALL METHOD cl_gui_frontend_services=>gui_download
EXPORTING
filename = l_file
write_field_separator = 'X'
CHANGING
data_tab = lt_data
EXCEPTIONS
file_write_error = 1
no_batch = 2
*..............
OTHERS = 24.
IF sy-subrc <> 0.
MESSAGE e899(mm) WITH 'Erro a gravar ficheiro'.
ENDIF.
Por sua vez upload é feito para esta tabela de output:
CALL METHOD cl_gui_frontend_services=>gui_upload
EXPORTING
filename = l_file
has_field_separator = 'X'
CHANGING
data_tab = lt_data
EXCEPTIONS
file_open_error = 1
file_read_error = 2
*..............
OTHERS = 19.
EXPORTING
filename = l_file
has_field_separator = 'X'
CHANGING
data_tab = lt_data
EXCEPTIONS
file_open_error = 1
file_read_error = 2
*..............
OTHERS = 19.
Agora os dados são passados da tabela de output para a tabela real, de input, pelo processo inverso de conversão:
DATA: lt_data TYPE TABLE OF t_data.
DATA: ls_data TYPE t_data.
DATA: l_col TYPE sy-tabix.
FIELD-SYMBOLS: <fs_field_in>, <fs_field_out>.
FIELD-SYMBOLS: <fs_t001w> TYPE t001w.
TYPES: BEGIN OF t_line,
fieldname TYPE dd03l-fieldname,
position TYPE dd03l-position,
END OF t_line.
DATA: lt_line TYPE TABLE OF t_line.
FIELD-SYMBOLS <fs_line> TYPE t_line.
*obtem lista de campos para exemplo
*num caso real teríamos por exemplo uma lista reduzida com 4 ou 5 campos
SELECT fieldname position INTO TABLE lt_line FROM dd03l
WHERE tabname = 'T001W' AND comptype = 'E'
ORDER BY position.
LOOP AT it_t001w ASSIGNING <fs_t001w>. "linha a linha
CLEAR ls_data.
LOOP AT lt_line ASSIGNING <fs_line>. "coluna a coluna
ADD 1 TO l_col.
ASSIGN COMPONENT <fs_line>-fieldname OF STRUCTURE <fs_t001w>
TO <fs_field_in>. "assigna campo in
ASSIGN COMPONENT l_col OF STRUCTURE ls_data
TO <fs_field_out>. "assigna campo out
*rotinas de conversão.
IF <fs_line>-fieldname = 'KUNNR' OR <fs_line>-fieldname = 'LIFNR'.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
input = <fs_field_in>
IMPORTING
output = <fs_field_out>.
ELSE. "outros elementos não sujeitos a conversão
<fs_field_out> = <fs_field_in>.
ENDIF.
ENDLOOP.
CLEAR l_col.
APPEND ls_data TO lt_data.
ENDLOOP.
DATA: ls_data TYPE t_data.
DATA: l_col TYPE sy-tabix.
FIELD-SYMBOLS: <fs_field_in>, <fs_field_out>.
FIELD-SYMBOLS: <fs_t001w> TYPE t001w.
TYPES: BEGIN OF t_line,
fieldname TYPE dd03l-fieldname,
position TYPE dd03l-position,
END OF t_line.
DATA: lt_line TYPE TABLE OF t_line.
FIELD-SYMBOLS <fs_line> TYPE t_line.
*obtem lista de campos para exemplo
*num caso real teríamos por exemplo uma lista reduzida com 4 ou 5 campos
SELECT fieldname position INTO TABLE lt_line FROM dd03l
WHERE tabname = 'T001W' AND comptype = 'E'
ORDER BY position.
LOOP AT it_t001w ASSIGNING <fs_t001w>. "linha a linha
CLEAR ls_data.
LOOP AT lt_line ASSIGNING <fs_line>. "coluna a coluna
ADD 1 TO l_col.
ASSIGN COMPONENT <fs_line>-fieldname OF STRUCTURE <fs_t001w>
TO <fs_field_in>. "assigna campo in
ASSIGN COMPONENT l_col OF STRUCTURE ls_data
TO <fs_field_out>. "assigna campo out
*rotinas de conversão.
IF <fs_line>-fieldname = 'KUNNR' OR <fs_line>-fieldname = 'LIFNR'.
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
input = <fs_field_in>
IMPORTING
output = <fs_field_out>.
ELSE. "outros elementos não sujeitos a conversão
<fs_field_out> = <fs_field_in>.
ENDIF.
ENDLOOP.
CLEAR l_col.
APPEND ls_data TO lt_data.
ENDLOOP.
Sem comentários:
Enviar um comentário