Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
nmirandaghn
Participant


Most of the time in SAP the answer for many things come from a lot of sources. The QR code implementation in SAP is an example of it.

At first, I went to google and I found this excellent example (How to create a QR code and show it in a Smartform) on scn.sap.com.



But then I realized that even if I use that code I had to deal with the fact that I should be aware that the QR code had to be generated from a Google API. That could be a problem because in my case I needed to use that feature internally, so it was not ok to access the internet to get the image. On the other hand, this site could be at any moment be closed or shutdown.



So, the thing is that I had to make my own webservice to get that image, and having a background of C# I found it logical to make my own webservice. So I began to investigate how to generate the QR and when I got it then I proceeded to code it. There are a lot of open source solutions out there and in my case, I just had to grab one of them and use it. I found a couple and the ZXing.Net implementation seemed to be the best in this case.



So I had to trick it a little so I could make it to work. One requirement of this webservice was that it had to make the QR code graphic on the fly. It was not ok to save a file representing the QR code in the webserver because eventually, it would have to take some kind of job or additional work on it to delete those files. The code for the webservice is the following:



using System.Drawing.Imaging;
using System.IO;
using System.Web;
using ZXing;
using ZXing.Common;

namespace lcqrcode
{
/// <summary>
/// Short description of Service
/// </summary>
public class Service : IHttpHandler
{
private const string NotAvailable = "Not available";
private const int MaxLength = 256;

public void ProcessRequest(HttpContext context)
{
if (context.Request.HttpMethod != "GET") return;

var qrValue = context.Request.QueryString["text"] ?? NotAvailable;

qrValue = qrValue.Trim();

if (qrValue.Length > MaxLength)
qrValue = qrValue.Substring(0, MaxLength - 1); // Limit to 'MaxLength' characters

var barcodeWriter = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = new EncodingOptions
{
Height = 200,
Width = 200,
PureBarcode = true
}
};

using (var bitmap = barcodeWriter.Write(qrValue))

using (var mem = new MemoryStream())
{
bitmap.SetResolution(50, 50);

bitmap.Save(mem, ImageFormat.Png);
mem.Seek(0, SeekOrigin.Begin);

context.Response.ContentType = "image/png";

mem.CopyTo(context.Response.OutputStream, 4096);
context.Response.Flush();
}
}

public bool IsReusable
{
get
{
return false;
}
}
}
}






Create a web project and insert a generic handler file (ASHX extension) in Visual Studio, name it 'Service', and paste the above code. Don't forget to include de ZXING reference in the project. I had to make use of this kind of file because I had to make the webservice restful.


The other step that I took was to make the ABAP code to easily use it in any smartform and the best way to do that was to make a BAPI. This approach would help to make use of it anytime anywhere.


So I took the code from above and encapsulated it in a BAPI. Here is the code (Taken and adapted from How to create a QR code and show it in a Smartform😞





function zqrcode .
*"----------------------------------------------------------------------
*"*"Interfase local
*" IMPORTING
*" REFERENCE(I_TEXT) TYPE CHAR255
*" REFERENCE(I_NAME) TYPE CHAR20
*" REFERENCE(I_WIDHT) TYPE I DEFAULT 200
*" REFERENCE(I_HEIGHT) TYPE I DEFAULT 200
*" EXPORTING
*" REFERENCE(E_CHECK) TYPE CHAR01
*"----------------------------------------------------------------------
perform get_qrcode using i_text changing e_check.
check e_check is not initial.

perform convert_image using i_name i_widht i_height changing e_check.
endfunction.



And here is the code for the Include of the BAPI (Taken and adapted from How to create a QR code and show it in a Smartform😞



*&---------------------------------------------------------------------*
*& Form GET_QRCODE
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* -->P_TEXT text
* -->P_CHECK text
*----------------------------------------------------------------------*
form get_qrcode using p_text type char255 changing p_check type c.
data oref type ref to cx_root.
data wa_source type zgen_qrsource.
select single * from zgen_qrsource into wa_source. " Get webservice address for QR code generation

check sy-subrc eq 0.

concatenate wa_source-address p_text into url.

call method cl_http_client=>create_by_url
exporting
url = url
importing
client = http_client
exceptions
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
others = 4.

if sy-subrc = 0.

* http_client->send( ).
call method http_client->send
exceptions
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3
http_invalid_timeout = 4.

* http_client->receive( ).
call method http_client->receive
exceptions
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3
others = 4.

check sy-subrc eq 0.

content = http_client->response->get_data( ).

http_client->close( ).

l_str_length = xstrlen( content ).

call function 'RSFO_XSTRING_TO_MIME'
exporting
c_xstring = content
i_length = l_str_length
tables
c_t_mime = mime.

p_check = 'X'.
endif.
endform. " download_qrcode

*&---------------------------------------------------------------------*
*& Form convert_image
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* -->P_NAME text
* -->P_HEIGHT text
* -->P_WIDTH text
* -->P_CHECK text
*----------------------------------------------------------------------*
form convert_image using p_name type char20 p_height type i p_width type i changing p_check type c.
create object i_igs_image_converter .

i_igs_image_converter->input = 'image/png'.
i_igs_image_converter->output = 'image/bmp'.
i_igs_image_converter->width = p_width.
i_igs_image_converter->height = p_height.

call method i_igs_image_converter->set_image
exporting
blob = mime
blob_size = l_content_length.

call method i_igs_image_converter->execute
exceptions
communication_error = 1
internal_error = 2
external_error = 3
others = 4.

if sy-subrc = 0.
call method i_igs_image_converter->get_image
importing
blob = blob
blob_size = blob_size
blob_type = blob_type.

p_check = 'X'.
else.
clear p_check.
endif.

if sy-subrc = 0.
perform generate_picture using p_name changing p_check.
endif.
endform. " CONVERT_IMAGE

*&---------------------------------------------------------------------*
*& Form generate_picture
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* -->P_NAME text
* -->P_CHECK text
*----------------------------------------------------------------------*
form generate_picture using p_name type char20 changing p_check type c.
gi_name = p_name.
gi_object = 'GRAPHICS'.
gi_id = 'BMAP'.
gi_btype = 'BCOL'. " If u want black and white pass BMON
gi_resident = ' '.
gi_autoheight = 'X'.
gi_bmcomp = 'X'.
l_extension = 'BMP'.

"importing the image into se78 before displaying it in the smartform.
perform import_bitmap_bds using blob
gi_name
gi_object
gi_id
gi_btype
l_extension
' '
gi_resident
gi_autoheight
gi_bmcomp
changing l_docid
gi_resolution.

if sy-subrc = 0.
p_check = 'X'.
else.
clear p_check.
endif.
endform. " SHOW_SMART_FORM

*&---------------------------------------------------------------------*
*& Form import_bitmap_bds
*&---------------------------------------------------------------------*
* text
*----------------------------------------------------------------------*
* -->P_BLOB text
* -->P_NAME text
* -->P_OBJECT text
* -->P_ID text
* -->P_BTYPE text
* -->P_FORMAT text
* -->P_TITLE text
* -->P_RESIDENT text
* -->P_AUTOHEIGHT text
* -->P_BMCOMP text
* -->P_DOCID text
* -->P_RESOLUTION text
*----------------------------------------------------------------------*
form import_bitmap_bds
using p_blob type w3mimetabtype
p_name type stxbitmaps-tdname
p_object type stxbitmaps-tdobject
p_id type stxbitmaps-tdid
p_btype type stxbitmaps-tdbtype
p_format type c
p_title like bds_description
p_resident type stxbitmaps-resident
p_autoheight type stxbitmaps-autoheight
p_bmcomp type stxbitmaps-bmcomp
changing p_docid type stxbitmaps-docid
p_resolution type stxbitmaps-resolution.


data: l_object_key type sbdst_object_key.
data: l_tab type ddobjname.

data: begin of l_bitmap occurs 0,
l(64) type x,
end of l_bitmap.

data: l_filename type string,
l_bytecount type i,
l_bds_bytecount type i.
data: l_color(1) type c,

l_width_tw type stxbitmaps-widthtw,
l_height_tw type stxbitmaps-heighttw,
l_width_pix type stxbitmaps-widthpix,
l_height_pix type stxbitmaps-heightpix.
data: l_bds_object type ref to cl_bds_document_set,
l_bds_content type sbdst_content,
l_bds_components type sbdst_components,
wa_bds_components type line of sbdst_components,
l_bds_signature type sbdst_signature,
wa_bds_signature type line of sbdst_signature,
l_bds_properties type sbdst_properties,
wa_bds_properties type line of sbdst_properties.

data wa_stxbitmaps type stxbitmaps.

* Enqueue
perform enqueue_graphic using p_object
p_name
p_id
p_btype.

* Bitmap conversion
call function 'SAPSCRIPT_CONVERT_BITMAP_BDS'
exporting
color = 'X'
format = p_format
resident = p_resident
bitmap_bytecount = l_bytecount
compress_bitmap = p_bmcomp
importing
width_tw = l_width_tw
height_tw = l_height_tw
width_pix = l_width_pix
height_pix = l_height_pix
dpi = p_resolution
bds_bytecount = l_bds_bytecount
tables
bitmap_file = p_blob
bitmap_file_bds = l_bds_content
exceptions
format_not_supported = 1
no_bmp_file = 2
bmperr_invalid_format = 3
bmperr_no_colortable = 4
bmperr_unsup_compression = 5
bmperr_corrupt_rle_data = 6
others = 7.

if sy-subrc <> 0.

perform dequeue_graphic using p_object
p_name
p_id
p_btype.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
raising conversion_failed.

endif.

* Save bitmap in BDS
create object l_bds_object.

wa_bds_components-doc_count = '1'.
wa_bds_components-comp_count = '1'.
wa_bds_components-mimetype = c_bds_mimetype.
wa_bds_components-comp_size = l_bds_bytecount.
append wa_bds_components to l_bds_components.

if p_docid is initial. " graphic is new

wa_bds_signature-doc_count = '1'.
append wa_bds_signature to l_bds_signature.


call method l_bds_object->create_with_table
exporting
classname = c_bds_classname
classtype = c_bds_classtype
components = l_bds_components
content = l_bds_content
changing
signature = l_bds_signature
object_key = l_object_key
exceptions
others = 1.

if sy-subrc <> 0.

perform dequeue_graphic using p_object
p_name
p_id
p_btype.
* message e285 with p_name 'BDS'.

endif.

read table l_bds_signature index 1 into wa_bds_signature
transporting doc_id.

if sy-subrc = 0.

p_docid = wa_bds_signature-doc_id.

else.

perform dequeue_graphic using p_object
p_name
p_id
p_btype.
* message e285 with p_name 'BDS'.

endif.

else. " graphic already exists

********* read object_key for faster access *****
clear l_object_key.
select single * from stxbitmaps into wa_stxbitmaps
where tdobject = p_object
and tdid = p_id
and tdname = p_name
and tdbtype = p_btype.

select single tabname from bds_locl into l_tab
where classname = c_bds_classname
and classtype = c_bds_classtype.

if sy-subrc = 0.
select single object_key from (l_tab) into l_object_key
where loio_id = wa_stxbitmaps-docid+10(32)
and classname = c_bds_classname
and classtype = c_bds_classtype.
endif.

******** read object_key end ********************
call method l_bds_object->update_with_table
exporting
classname = c_bds_classname
classtype = c_bds_classtype
object_key = l_object_key
doc_id = p_docid
doc_ver_no = '1'
doc_var_id = '1'
changing
components = l_bds_components
content = l_bds_content
exceptions
nothing_found = 1
others = 2.

if sy-subrc = 1. " inconsistency STXBITMAPS - BDS; repeat check in

wa_bds_signature-doc_count = '1'.
append wa_bds_signature to l_bds_signature.

call method l_bds_object->create_with_table
exporting
classname = c_bds_classname
classtype = c_bds_classtype
components = l_bds_components
content = l_bds_content
changing
signature = l_bds_signature
object_key = l_object_key
exceptions
others = 1.

if sy-subrc <> 0.
perform dequeue_graphic using p_object
p_name
p_id
p_btype.
* message e285 with p_name 'BDS'.

endif.

read table l_bds_signature index 1 into wa_bds_signature
transporting doc_id.
if sy-subrc = 0.
p_docid = wa_bds_signature-doc_id.
else.

perform dequeue_graphic using p_object
p_name
p_id
p_btype.

* message e285 with p_name 'BDS'.

endif.

elseif sy-subrc = 2.

perform dequeue_graphic using p_object
p_name
p_id
p_btype.

* message e285 with p_name 'BDS'.

endif.

endif.

* Save bitmap header in STXBITPMAPS
wa_stxbitmaps-tdname = p_name.
wa_stxbitmaps-tdobject = p_object.
wa_stxbitmaps-tdid = p_id.
wa_stxbitmaps-tdbtype = p_btype.
wa_stxbitmaps-docid = p_docid.
wa_stxbitmaps-widthpix = l_width_pix.
wa_stxbitmaps-heightpix = l_height_pix.
wa_stxbitmaps-widthtw = l_width_tw.
wa_stxbitmaps-heighttw = l_height_tw.
wa_stxbitmaps-resolution = p_resolution.
wa_stxbitmaps-resident = p_resident.
wa_stxbitmaps-autoheight = p_autoheight.
wa_stxbitmaps-bmcomp = p_bmcomp.
insert into stxbitmaps values wa_stxbitmaps.

if sy-subrc <> 0.

update stxbitmaps from wa_stxbitmaps.

if sy-subrc <> 0.
* message e285 with p_name 'STXBITMAPS'.
endif.
endif.

* Set description in BDS attributes
wa_bds_properties-prop_name = 'DESCRIPTION'.
wa_bds_properties-prop_value = p_title.
append wa_bds_properties to l_bds_properties.
call method l_bds_object->change_properties
exporting
classname = c_bds_classname
classtype = c_bds_classtype
object_key = l_object_key
doc_id = p_docid
doc_ver_no = '1'
doc_var_id = '1'
changing
properties = l_bds_properties
exceptions
others = 1.

perform dequeue_graphic using p_object
p_name
p_id
p_btype.
endform. "import_bitmap_bds

*&---------------------------------------------------------------------*
*& Form ENQUEUE_GRAPHIC
*&---------------------------------------------------------------------*
* Enqueue of graphics stored in BDS
*----------------------------------------------------------------------*
form enqueue_graphic using p_object
p_name
p_id
p_btype.
call function 'ENQUEUE_ESSGRABDS'
exporting
* MODE_STXBITMAPS = 'E'
tdobject = p_object
tdname = p_name
tdid = p_id
tdbtype = p_btype
* X_TDOBJECT = ' '
* X_TDNAME = ' '
* X_TDID = ' '
* X_TDBTYPE = ' '
* _SCOPE = '2'
* _WAIT = ' '
* _COLLECT = ' '
exceptions
foreign_lock = 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
raising enqueue_failed.
endif.

endform. " ENQUEUE_GRAPHIC

*&---------------------------------------------------------------------*
*& Form DEQUEUE_GRAPHIC
*&---------------------------------------------------------------------*
* Dequeue of graphics stored in BDS
*----------------------------------------------------------------------*
form dequeue_graphic using p_object
p_name
p_id
p_btype.

call function 'DEQUEUE_ESSGRABDS'
exporting
* MODE_STXBITMAPS = 'E'
* X_TDOBJECT = ' '
* X_TDNAME = ' '
* X_TDID = ' '
* X_TDBTYPE = ' '
* _SCOPE = '3'
* _SYNCHRON = ' '
* _COLLECT = ' '
tdobject = p_object
tdname = p_name
tdid = p_id
tdbtype = p_btype.

endform. " DEQUEUE_GRAPHIC



Next, we have to make the output on a smartform and it's very simple. Just take the BAPI and use it inside the smartform and to avoid that file names collide, use the 'GUID_CREATE' function module to generate different names.


This function module has 3 importing parameters which output 3 kinds of GUIDs. In my case, I used the 'EV_GUID_16' one. The base of this strategy is that there will be no chance to have the same file name per request.



data: lv_text type char255, " Variable for text to convert in QR code
lv_check type c, " Flag to check if QR code was generated
lv_name type char20. " Variable for file name

data lv_guid type guid_16. " Variable for GUID
call function 'GUID_CREATE'
importing
ev_guid_16 = lv_guid.
move lv_guid to lv_name.

lv_text = 'Demo for QR code'.

call function 'ZQRCODE'
exporting
i_text = lv_text " Text to convert
i_name = lv_name " Graphic name
importing
e_check = lv_check. " 'X' = success in conversion

if lv_check is not initial.
g_name = lv_name.
display = 'X'. " Yes, display the image
endif.





The steps in SAP for the smartform are:

1. Execute transaction 'smartforms'.

2. Create a smartform





3. Define global variables





4. Add 'Program Lines'





5. Paste the above code into '%CODE1'





6. Insert a graphic object and in 'General Attributes' table type '&G_NAME& in Name, 'GRAPHICS' in Object and 'BMAP' in ID properties.







7. Finally in 'Conditions' tab add the variable 'DISPLAY' with comparison value = 'X'. This variable is defined in step 3.







Just consider that in this case the images are stored in SAP. You can check those files in transaction 'SE78'.




The only step left was to make the webservice configurable, which meant that I had to keep in a table in SAP the webservice address. This strategy is good because it provides a flexible way to maintain the webservice in any server.

You can check that in the form 'DOWNLOAD_QRCODE' in the include file of the BAPI:





form download_qrcode using p_text type char255 changing p_check type c.
data oref type ref to cx_root.
data wa_source type zyour_table.
select single * from zyour_table into wa_source. " Get webservice address for QR code generation


check sy-subrc eq 0.

concatenate wa_source-address p_text into url.

call method cl_http_client=>create_by_url
EXPORTING
url = url
IMPORTING
client = http_client
EXCEPTIONS
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
others = 4.

if sy-subrc = 0.





This code will allow us to indicate the web address of the webservice and change it anytime I want.

Check the output of the smartform pressing 3 times the F8 key and then clicking on 'Print preview' button in the Print dialog:







Smartform can be very tricky but they are very handy and powerful. A lot of transactions in SAP ECC use these reports and you can define your own in transaction NACE, they come with all the information and information that are needed to adapt them according to the client requirements.


And the other hand you can make your own smartforms and use them in your own programs with the same tools in SAP.


See you in the next post.

3 Comments