PDF Base 64 (string) to SAP FBL1N
Hello all Abapers,
I did something simple in SAP, but I did not find everything in one topic only. So I decided to do this post and I’ll try to explain it as easily as possible.
The problem was: “I have a PDF file in the portal and I need to attach it to the FBL1N transaction.”
The tools used were SAP ECC 618 EHP 6, Abap Netweaver 7.51, SAP PO 7.5 SP01 and any portal with API Rest that returns a JSON.
Doing some research I discovered that the best way to transpose a PDF is to use PDF converted to Base64, but as a string and not binary. For the binary does not recognize some special characters of our handwriting.
Conversion from PDF to Base64 (STRING) can be done easily via code (http://bit.ly/2Ft4vPE in JS) or by using some sites that already do this (http://bit.ly/2YgBCNP), the result is quite different from the conventional, more or less this:
The communication to integrate with the PO was done in NodeJS and I used Ngrok to make the reverse proxy. Simple, easy and fast. I used the Visual Studio Code to make the code.
Something like this http://bit.ly/2Jw1ORj.
In Enterprise Builder:
In this image already shows much of what was done. All fields are strings as we are using the Base64 string and not the binary.
In my scenario I do not send anything to do the “GET”, but the PO requires that we pass some information, so I’m passing a dummy
In Integration Builder, it was just the configuration of ICO to connect with Ngrok. Remember that every time Ngrok is initialized, the URL of the Communication Channel must be changed. See image below.
Let’s now see the part that matters, the ABAP part.
In the SPORXY transaction we create all the PO objects that will be needed for reading the NodeJS.
Then in the program is created the reading of the proxy that will get to the 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.
The Input will come from the Base64 string. The first thing to do is to execute the SSFC_BASE64_DECODE function.
DATA twebpdf TYPE string. DATA fic_binario TYPE xstring. twebpdf = base64. CALL FUNCTION 'SSFC_BASE64_DECODE' EXPORTING b64data = twebpdf IMPORTING bindata = fic_binario.
Here we will convert the string to xstring, which is the string that SAP understands to do the conversion.
Soon after we will create a table of type sdokcntbin and put the base64 inside it. Taking advantage of the Try already put the OPEN DATASET in it, because I will generate in the server the file. If you are generating the local file you can use the GUI_DOWNLOAD for binary files.
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.
If you used GUI_DOWNLOAD you will have the PDF already converted, if you used the OPEN DATASET you can get the PDF using CG3Y or AL11. Remember the format is always BIN.
We have not yet attached to FBL1N, but we already have the file ready.
Basically we will have to read via OPEN DATASET, or GUI_UPLOAD, the file and store in a table type soli and convert the entire contents to binary.
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.
After that we have to get the ID of the folder that SAP will store to show in FBL1N, for this we use the SO_FOLDER_ROOT_ID_GET function. Use as private because shared is not recognized by the transaction.
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.
The next step is to separate the file extension from the name. There are thousands of ways to do this I’ve used a function, you can do as you see fit. With the done split we will store in the objheader_tb table concatenated ‘& SO_FILENAME = “and the name of the file. This will cause the transaction to understand the file that will be opened.
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.
Next we will execute the SO_OBJECT_INSERT function, but before we need to fill in the prerequisite structures of it. Objheader_tb was populated up and content_tb higher up.
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.
And to finish we just need to execute the BINARY_RELATION_CREATE COMMIT function, followed by a 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.
Okay, we have a PDF attached to FBL1N.