Technical Articles
PDF Base 64 (string) para SAP FBL1N
Olá a todos Abapers Brasil,
Fiz algo simples no SAP, mas não encontrei tudo em um tópico somente. Então resolvi fazer esse post e irei tentar explicar do modo mais fácil possível.
O problema era: “tenho um arquivo PDF no portal e preciso anexa-lo na transação FBL1N”.
As ferramentas utilizadas foram SAP ECC 618 EHP 6, Abap Netweaver 7.51, SAP PO 7.5 SP01 e um portal qualquer com API Rest que retorne um JSON.
Fazendo algumas pesquisas descobri que a melhor forma de transitar um PDF é utilizando o PDF convertido em Base64 porém como string e não binário. Pois o binário não reconhece alguns caracteres especiais da nossa caligrafia.
A conversão de PDF para Base64 (STRING) pode ser feita facilmente via código (http://bit.ly/2Ft4vPE em JS) ou utilizando alguns sites que já fazem isso (http://bit.ly/2YgBCNP), o resultado é bem diferente do convencional, mais ou menos isso:
A comunicação para integrar com o PO foi feito no NodeJS e utilizei o Ngrok para fazer o proxy reverso. Simples, fácil e rápido e utilizei o Visual Studio Code para fazer o código.
Algo parecido com isso http://bit.ly/2Jw1ORj.
O PO é a parte mais simples de fazer.
No Enterprise Builder:
Nessa imagem já mostra boa parte do que foi feito. Todos os campos são strings pois estamos utilizando o Base64 string e não a binária.
Operation Mapping
No meu cenário eu não envio nada para fazer o get, porém o PO exige que passamos alguma informação, por isso estou passando um dummy.
No Integration Builder, foi somente a configuração do ICO para conectar com o Ngrok. Lembrado que toda a vez que o Ngrok é inicializado tem que ser alterada a URL do Communication Channel. Veja imagem abaixo.
Vamos agora ver a parte que interessa, a parte ABAP.
Na transação SPORXY criamos todos os objetos do PO que serão necessário para a leitura do NodeJS.
Depois no programa é criado a leitura do proxy que irá chegar até o NodeJS.
DATA output TYPE update_pdf_dummy .
DATA input TYPE return_update_pdf_sa.
TRY.
DATA(Base64_pdf) = NEW outbound_update_pdf( ).
Base64_pdf->si_outbound_update_pdf_titulos(
EXPORTING
output = output
IMPORTING
input = input ).
CATCH cx_ai_system_fault INTO DATA(system_fault).
BREAK-POINT.
ENDTRY.
O Resultado (input) virá o Base64 em string. A primeira coisa a fazer é executar a função SSFC_BASE64_DECODE.
DATA twebpdf TYPE string.
DATA fic_binario TYPE xstring.
twebpdf = base64.
CALL FUNCTION 'SSFC_BASE64_DECODE'
EXPORTING
b64data = twebpdf
IMPORTING
bindata = fic_binario.
Aqui iremos converter a string para xstring, que é a string que o SAP entende para fazer a conversão.
Logo após vamos criar uma tabela do tipo sdokcntbin e colocar o base64 dentro dela. Aproveitando o Try já coloquei o OPEN DATASET nele, pois irei gerar no servidor o arquivo. Se for gerar o arquivo local pode usar a GUI_DOWNLOAD para arquivos binários.
DATA contents_tb TYPE TABLE OF sdokcntbin.
DATA contents_st TYPE sdokcntbin.
DATA file_length TYPE i.
DATA flag TYPE c.
DATA off TYPE i.
DATA len TYPE i.
DATA file_name TYPE string VALUE '/usr/sap/tmp/'.
TRY.
len = xstrlen( fic_binario ).
file_length = len.
WHILE flag IS INITIAL.
IF len LE 1022.
contents_st-line = fic_binario+off(len).
flag = 'X'.
ELSE.
contents_st-line = fic_binario+off(1022).
off = off + 1022.
len = len - 1022.
ENDIF.
APPEND contents_st TO contents_tb.
ENDWHILE.
CONCATENATE file_name filename INTO file_name.
CONDENSE file_name NO-GAPS.
OPEN DATASET file_name FOR OUTPUT IN BINARY MODE.
LOOP AT contents_tb ASSIGNING FIELD-SYMBOL(<contents_fs>).
TRANSFER <contents_fs> TO file_name.
ENDLOOP.
CLOSE DATASET file_name.
ENDTRY.
Se você utilizou o GUI_DOWNLOAD irá ter o PDF já convertido, se utilizou o OPEN DATASET pode pegar o PDF utilizando a CG3Y ou na AL11. Lembre-se o formato é sempre BIN.
Não anexamos ainda na FBL1N, mas já temos o arquivo pronto.
Basicamente teremos que ler via OPEN DATASET, ou GUI_UPLOAD, o arquivo e armazenar em uma tabela tipo soli e converter todo o conteúdo para binário.
DATA content_tb TYPE STANDARD TABLE OF soli.
DATA content_st TYPE soli.
DATA file TYPE string VALUE '/usr/sap/tmp/'.
CONCATENATE file filename INTO file.
OPEN DATASET file FOR INPUT IN BINARY MODE.
WHILE sy-subrc = 0.
READ DATASET file INTO content_st.
APPEND content_st TO content_tb.
ENDWHILE.
CLOSE DATASET file.
CALL FUNCTION 'SO_CONVERT_CONTENTS_BIN'
EXPORTING
it_contents_bin = content_tb[]
IMPORTING
et_contents_bin = content_tb[].
Depois disso temos que pegar o ID da pasta que o SAP irá armazenar para mostrar na FBL1N, para isso usamos a função SO_FOLDER_ROOT_ID_GET. Utilizar como privado pois a compartilhada não é reconhecida pela transação.
DATA folder_id TYPE soodk.
CALL FUNCTION 'SO_FOLDER_ROOT_ID_GET'
EXPORTING
region = 'B' “Privado
IMPORTING
folder_id = folder_id
EXCEPTIONS
communication_failure = 1
owner_not_exist = 2
system_failure = 3
x_error = 4
OTHERS = 5.
O próximo passo é separar a extensão do arquivo do nome. Existem milhares de formas de fazer isso eu usei uma função, pode fazer como achar melhor. Com o split feito iremos armazenar na tabela objheader_tb concatenado ‘&SO_FILENAME=” e o nome do arquivo. Isso irá fazer com que a transação entenda o arquivo que irá ser aberto.
DATA objhead_tb TYPE STANDARD TABLE OF soli.
DATA file_name TYPE c LENGTH 100.
DATA extension TYPE c LENGTH 4.
CALL FUNCTION 'CH_SPLIT_FILENAME'
EXPORTING
complete_filename = file
IMPORTING
extension = extension
name_with_ext = file_name
EXCEPTIONS
invalid_drive = 1
invalid_path = 2
OTHERS = 3.
IF sy-subrc EQ 0.
APPEND INITIAL LINE TO objhead_tb ASSIGNING FIELD-SYMBOL(<objhead_fs>).
CONCATENATE '&SO_FILENAME=' file_name INTO <objhead_fs>.
ENDIF.
A seguir iremos executar a função SO_OBJECT_INSERT, mas antes precisamos preencher as estruturas de pré-requisito dela. A objheader_tb foi preenchida acima e a content_tb mais acima.
DATA content_tb TYPE STANDARD TABLE OF soli.
DATA objhead_tb TYPE STANDARD TABLE OF soli.
DATA object_st TYPE borident.
DATA obj_id TYPE soodk.
DATA content_st TYPE soli.
DATA obj_data_st TYPE sood1.
object_st-objkey = objid.
object_st-objtype = 'BSEG'.“BSEG pq é FBL1N outras transações isso muda
obj_data_st-objsns = 'O'.
obj_data_st-objla = sy-langu.
obj_data_st-objdes = 'Attachment by Thiago'.
obj_data_st-file_ext = extension.
TRANSLATE obj_data_st-file_ext TO UPPER CASE.
obj_data_st-objlen = lines( content_tb ) * 255.
CALL FUNCTION 'SO_OBJECT_INSERT'
EXPORTING
folder_id = folder_id
object_type = 'EXT'
object_hd_change = obj_data_st
IMPORTING
object_id = obj_id
TABLES
objhead = objhead_tb
objcont = content_tb
EXCEPTIONS
active_user_not_exist = 1
communication_failure = 2
component_not_available = 3
dl_name_exist = 4
folder_not_exist = 5
folder_no_authorization = 6
object_type_not_exist = 7
operation_no_authorization = 8
owner_not_exist = 9
parameter_error = 10
substitute_not_active = 11
substitute_not_defined = 12
system_failure = 13
x_error = 14
OTHERS = 15.
E para finalizar só precisaremos executar a função BINARY_RELATION_CREATE_COMMIT, seguida de um COMMIT WORK.
DATA folmem_k_st TYPE sofmk.
DATA note_st TYPE borident.
DATA ep_note TYPE borident-objkey.
folmem_k_st-foltp = folder_id-objtp.
folmem_k_st-folyr = folder_id-objyr.
folmem_k_st-folno = folder_id-objno.
folmem_k_st-doctp = folder_id-objtp.
folmem_k_st-docyr = folder_id-objyr.
folmem_k_st-docno = folder_id-objno.
ep_note = folmem_k_st.
note_st-objtype = 'MESSAGE'.
note_st-objkey = ep_note.
CALL FUNCTION 'BINARY_RELATION_CREATE_COMMIT'
EXPORTING
obj_rolea = object_st
obj_roleb = note_st
relationtype = 'ATTA'
EXCEPTIONS
no_model = 1
internal_error = 2
unknown = 3
OTHERS = 4.
IF sy-subrc EQ 0.
COMMIT WORK.
ENDIF.
Pronto, temos um PDF anexado na FBL1N.
Abraço a todos,