Technical Articles
Asynchronous Posting & Dumping of production versions XL Data
Introduction:
In this blog post, we’ll come to know that posting and downloading of production versions of material XL Data.
Posting the XL data into SAP was done using a Start immediately Update Function module CM_FV_PROD_VERS_DB_UPDATE from presentation layer and dumping the XL data from SAP into Presentation layer was done using a normal function module GUI_DOWNLOAD.
PROCEDURE:
Step 1:
Go to Transaction SE38.
Enter the name of the program and click on create button as shown below.
The below Screen will appear give the program type as executable.
Click on save button
Step 2:
Designed a selection screen for Uploading, Downloading and ALV Output Functionalities.
When we Un-Check the Upload check box it will work for download and if we check the check box it will work for upload.
Check Upload Check-Box:
Uncheck Upload Check-Box:
Value request and help request logic was handled to load the XL data from Presentation layer. Enabling and disabling the Selection screen fields are handled at selection-screen output event.
Step 3:
When the upload check box is Unchecked, data displayed in ALV format for download using REUSE_ALV_GRID_DISPLAY Function module.
Pass the filled field-catalogs, PF Status Event, User-command event to ALV Function module to display the output.
Test Case:
Input:
Execute the report it will display the ALV output.
Output:
In the application tool bar you will find the download button to download data into Presentation layer in XL Format.
Step 4 :
If we press the download button an XL File got downloaded into presentation layer.
Note: Please maintain .XLS at the end of the file name.
Dumped XL Data:
Step 5 :
If checkbox was checked to load the XL data from presentation layer to SAP.
Presentation layer XL Data:
Test Case:
Selection screen :
Execute the report it will update the Production data into SAP.
Function module used to arrange the XL Data is ALSM_EXCEL_TO_INTERNAL_TABLE .
Function module used to update the production vertions data into sap Table MKAL is CM_FV_PROD_VERS_DB_UPDATE in UPDATE TASK Mode.
Step 6 :
Please find the logic to dump and posting the production versions data.
TYPE-POOLS slis.
**– Types Declaration
TYPES: BEGIN OF ty_mkal, ” internal table as per flat file strucre
werks TYPE mkal-werks, ” Plant
matnr TYPE mkal-matnr, ” Material Number
verid TYPE mkal-verid, ” Production Version
text1 TYPE mkal-text1, ” Short text on the production version
adatu TYPE mkal-adatu, ” Valid-from date of production version
bdatu TYPE mkal-bdatu, ” Run-time end: production version
bstmi TYPE mkal-bstmi, ” Lower value of the lot-size interval
bstma TYPE mkal-bstma, ” Upper value of the lot-size interval
stlal TYPE mkal-stlal, ” Alternative BOM
stlan TYPE mkal-stlan, ” BOM Usage
plnnr TYPE mkal-plnnr, ” Key for Task List Group
alnal TYPE mkal-alnal, ” Group Counter
plnty TYPE mkal-plnty, ” Task List Type
END OF ty_mkal,
BEGIN OF ty_mkal1.
INCLUDE STRUCTURE mkal.
TYPES END OF ty_mkal1.
TYPES : BEGIN OF ty_fname,
fname(20) TYPE c,
END OF ty_fname.
**– Global Variables
DATA : v1 TYPE mkal-werks,
v2 TYPE mkal-matnr,
lv_file TYPE string,
r_comm TYPE string,
v_matnr TYPE matnr,
v_werks TYPE werks_d.
**– Work Areas & Inernal Tables
DATA : it_mkal TYPE alsmex_tabline OCCURS 0 WITH HEADER LINE,
ls_fieldcat TYPE slis_fieldcat_alv,
t_fieldcatelog TYPE TABLE OF slis_fieldcat_alv,
it_it_mkal_i TYPE STANDARD TABLE OF ty_mkal1,
wa_it_mkal_i LIKE LINE OF it_it_mkal_i,
it_m_aend TYPE STANDARD TABLE OF mkal_aend WITH HEADER LINE,
it_mkal2 TYPE TABLE OF ty_mkal WITH HEADER LINE,
wa_mkal2 TYPE ty_mkal,
ls_fname TYPE ty_fname,
lt_fname TYPE STANDARD TABLE OF ty_fname,
c TYPE slis_selfield,
a TYPE slis_t_extab,
it_event TYPE slis_t_event,
wa_event LIKE LINE OF it_event.
**– Selection screen Design
SELECTION-SCREEN BEGIN OF BLOCK a WITH FRAME TITLE TEXT-001.
SELECT-OPTIONS :s_plant FOR v1 MODIF ID m1,
s_matnr FOR v2 MODIF ID m1.
SELECTION-SCREEN SKIP.
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS p_upload AS CHECKBOX USER-COMMAND uc DEFAULT ‘X’.
SELECTION-SCREEN COMMENT 3(7) TEXT-002.
PARAMETERS p_file TYPE ibipparms-path MODIF ID m2 .
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN END OF BLOCK a.
AT SELECTION-SCREEN.
SELECT SINGLE matnr
werks FROM mkal
INTO ( v_matnr , v_werks )
WHERE matnr IN s_matnr
AND werks IN s_plant.
IF sy-subrc NE 0.
MESSAGE TEXT-004 TYPE ‘E’.
ENDIF.
AT SELECTION-SCREEN OUTPUT.
IF p_upload IS NOT INITIAL.
LOOP AT SCREEN.
IF screen-group1 = ‘M1’.
screen-input = 0.
MODIFY SCREEN.
ELSEIF screen-group1 = ‘M2’.
screen-input = 1.
MODIFY SCREEN.
ENDIF.
ENDLOOP.
ELSE.
LOOP AT SCREEN.
IF screen-group1 = ‘M1’.
screen-input = 1.
MODIFY SCREEN.
ELSEIF screen-group1 = ‘M2’.
screen-input = 0.
MODIFY SCREEN.
ENDIF.
ENDLOOP.
ENDIF.
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. ” f4 help for file input
PERFORM file_help.
*
AT SELECTION-SCREEN ON HELP-REQUEST FOR p_file. ” F1 help for file input
MESSAGE ‘Please press f4 to upload the file’ TYPE ‘I’.
START-OF-SELECTION.
IF p_upload IS INITIAL.
IF ( s_plant-low IS NOT INITIAL OR s_plant-high IS NOT INITIAL ) AND
( s_matnr-low IS NOT INITIAL OR s_matnr-high IS NOT INITIAL ).
SELECT matnr
werks
verid
bdatu
adatu
stlal
stlan
plnnr
alnal
text1
bstmi
bstma
plnty FROM mkal INTO CORRESPONDING FIELDS OF TABLE it_mkal2 WHERE werks IN s_plant AND matnr IN s_matnr.
IF sy-subrc NE 0.
MESSAGE TEXT-003 TYPE ‘E’.
ELSE.
* Filling of Fieldcatalog
ls_fieldcat-col_pos = 1.
ls_fieldcat-fieldname = ‘WERKS’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘PLANT’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m.
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 2.
ls_fieldcat-fieldname = ‘MATNR’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘MATERIAL’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 3.
ls_fieldcat-fieldname = ‘VERID’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘VESION ID’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 4.
ls_fieldcat-fieldname = ‘TEXTL’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘TEXT’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 5.
ls_fieldcat-fieldname = ‘ADATU’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘VALID FROM’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 6.
ls_fieldcat-fieldname = ‘BDATU’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘VALID TO’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m.
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 7.
ls_fieldcat-fieldname = ‘BSTMI’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘LOWER LOT SIZE’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 8.
ls_fieldcat-fieldname = ‘BSTMA’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘UPPER LOT SIZE’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 9.
ls_fieldcat-fieldname = ‘STLAL’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘ALT BOM’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 10.
ls_fieldcat-fieldname = ‘STLAN’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘BOM’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 11.
ls_fieldcat-fieldname = ‘PLNNR’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘GROUP KEY’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 12.
ls_fieldcat-fieldname = ‘ALNAL’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘GROUP COUNTER ‘.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
ls_fieldcat-col_pos = 13.
ls_fieldcat-fieldname = ‘PLNTY’.
ls_fieldcat-tabname = ‘IT_MKAL2’.
ls_fieldcat-seltext_m = ‘TASK LIST TYPE’.
APPEND ls_fieldcat TO t_fieldcatelog.
ls_fname-fname = ls_fieldcat-seltext_m .
APPEND ls_fname TO lt_fname.
CLEAR: ls_fieldcat,ls_fname.
wa_event-name = ‘PF_STATUS_SET’.
wa_event-form = ‘ZGUI’.
PERFORM zgui USING a.
APPEND wa_event TO it_event.
wa_event-name = ‘USER_COMMAND’.
wa_event-form = ‘UC’.
PERFORM uc USING sy-ucomm c.
APPEND wa_event TO it_event.
CALL FUNCTION ‘REUSE_ALV_GRID_DISPLAY’
EXPORTING
i_callback_program = sy-repid
i_structure_name = ‘WA_MKAL’
it_fieldcat = t_fieldcatelog[]
it_events = it_event
TABLES
t_outtab = it_mkal2
EXCEPTIONS
program_error = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF. ” SET_PF_STATUS
ENDIF.
ENDIF.
ELSE.
CALL FUNCTION ‘ALSM_EXCEL_TO_INTERNAL_TABLE’
EXPORTING
filename = p_file
i_begin_col = 1
i_begin_row = 1
i_end_col = 13
i_end_row = 99
TABLES
intern = it_mkal
EXCEPTIONS
inconsistent_parameters = 1
upload_ole = 2
OTHERS = 3.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
FIELD-SYMBOLS : <fs>.
DATA l_index TYPE i.
IF it_mkal[] IS INITIAL.
MESSAGE ‘No data uploaded!’ TYPE ‘E’.
EXIT.
ELSE.
SORT it_mkal BY row col.
LOOP AT it_mkal.
MOVE it_mkal-col TO l_index.
ASSIGN COMPONENT l_index OF STRUCTURE it_mkal2 TO <fs>.
IF sy-subrc = 0. ” Incase there are more xls columns than fields
MOVE it_mkal-value TO <fs>.
ENDIF.
AT END OF row.
APPEND it_mkal2.
CLEAR it_mkal2.
ENDAT.
ENDLOOP.
ENDIF.
” populate fields of struture and append to itab
LOOP AT it_mkal2.
wa_it_mkal_i-mandt = sy-mandt.
CALL FUNCTION ‘CONVERSION_EXIT_ALPHA_INPUT’
EXPORTING
input = it_mkal2-matnr
IMPORTING
output = it_mkal2-matnr.
MOVE-CORRESPONDING it_mkal2 TO wa_it_mkal_i.
APPEND wa_it_mkal_i TO it_it_mkal_i.
CLEAR : wa_it_mkal_i.
ENDLOOP.
LOOP AT it_mkal2.
it_m_aend-matnr = it_mkal2-matnr.
it_m_aend-werks = it_mkal2-werks.
it_m_aend-verid = it_mkal2-verid.
it_m_aend-zaehl = ‘0001’.
APPEND it_m_aend.
CLEAR it_m_aend.
ENDLOOP.
IF it_it_mkal_i IS NOT INITIAL.
CALL FUNCTION ‘CM_FV_PROD_VERS_DB_UPDATE’ IN UPDATE TASK
TABLES
it_mkal_i = it_it_mkal_i.
COMMIT WORK AND WAIT.
IF sy-subrc EQ 0.
WRITE:2 TEXT-005.
ENDIF.
ENDIF.
ENDIF.
FORM file_help .
CALL FUNCTION ‘F4_FILENAME’
EXPORTING
program_name = syst-cprog
dynpro_number = syst-dynnr
field_name = ‘P_FILE’
IMPORTING
file_name = p_file.
ENDFORM. ” FILE_HELP
*&———————————————————————*
*& Form ZGUI
*&———————————————————————*
* text
*———————————————————————-*
* –>P_A text
*———————————————————————-*
FORM zgui USING p_a TYPE slis_t_extab.
SET PF-STATUS ‘STAT’.
ENDFORM.
*&———————————————————————*
*& Form UC
*&———————————————————————*
* text
*———————————————————————-*
* –>P_SY_UCOMM text
* –>P_SLIS_SELFIELD text
*———————————————————————-*
*&———————————————————————*
*& Form UC
*&———————————————————————*
* text
*———————————————————————-*
* –>P_SY_UCOMM text
* –>P_C text
*———————————————————————-*
FORM uc USING a LIKE sy-ucomm
b TYPE slis_selfield.
IF a = ‘DOWN’.
DATA p_fname TYPE ibipparms-path.
CALL FUNCTION ‘F4_FILENAME’
EXPORTING
program_name = syst-cprog
dynpro_number = syst-dynnr
field_name = ‘P_FNAME’
IMPORTING
file_name = p_fname.
DATA p_name TYPE string.
p_name = p_fname.
CALL FUNCTION ‘GUI_DOWNLOAD’
EXPORTING
* BIN_FILESIZE =
filename = p_name
filetype = ‘ASC’
append = ‘X’
write_field_separator = ‘X’
* HEADER = ’00’
* TRUNC_TRAILING_BLANKS = ‘ ‘
* WRITE_LF = ‘X’
* COL_SELECT = ‘ ‘
* COL_SELECT_MASK = ‘ ‘
* DAT_MODE = ‘ ‘
* CONFIRM_OVERWRITE = ‘ ‘
* NO_AUTH_CHECK = ‘ ‘
* CODEPAGE = ‘ ‘
* IGNORE_CERR = ABAP_TRUE
* REPLACEMENT = ‘#’
* WRITE_BOM = ‘ ‘
* TRUNC_TRAILING_BLANKS_EOL = ‘X’
* WK1_N_FORMAT = ‘ ‘
* WK1_N_SIZE = ‘ ‘
* WK1_T_FORMAT = ‘ ‘
* WK1_T_SIZE = ‘ ‘
* WRITE_LF_AFTER_LAST_LINE = ABAP_TRUE
* SHOW_TRANSFER_STATUS = ABAP_TRUE
* VIRUS_SCAN_PROFILE = ‘/SCET/GUI_DOWNLOAD’
* IMPORTING
* FILELENGTH =
TABLES
data_tab = it_mkal2
fieldnames = lt_fname
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
OTHERS = 22.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
ENDIF.
ENDFORM.
Conclusion:
Using this report we can update Productions Versions of material table data with XL Data using ‘CM_FV_PROD_VERS_DB_UPDATE‘ Function module and download the updated XL Data to Presentation layer.
Thanks for reading.
Hope this post would be helpful.
Have you look at abap2xlsx? It has some amazing capabilities including uploading Excel sheets. It saves a lot of work.
I'm not sure what version you are on, but using classes is always a good options. First to get rid of your performs. The class CL_GUI_FRONTEND_SERVICES is a nice one to use.
Now for the positive comments:
Nice blog. Just some changes to upgrade the code. It's hard to write blogs like this with a ton of code reviews looking at your code.
I'm so sorry that, if I google "excel" "download" site:blogs.sap.com, it doesn't return your blog post, because you only use the abbrevation XL.
Sorry, I feel Michelle Crapo is being very generous calling this "nice blog". Personally, I'm terrified that someone would read this and think this is a good example of an SCN blog or even a good ABAP example. This post wants me go full-on Gordon Ramsay and start screaming profanities. But that would not be very professional or community-friendly. So to borrow OJ Simpson's "if I did it, that's how I would've done it" format, if I were to write such blog, I would've done the following.
I think a lot of it is around what version of SAP that you are on. Sometimes you don't have an option to use something else. I didn't notice the name/description differences - good catch.
I do like the format of the blog. A snippet of the code and then the explanation. I think that if you've never written a program - those early steps would help. But again it depends on your version of SAP. SE38 or SE80 isn't a good start point for people starting their programming careers. I would start them with Eclipse.
😉 Nice blog is in the eyes of the beholder. Again I liked the format - snippet and explanation. I would be upset as a code reviewer if I saw this in my company. However, we use on-premise HANA. So I would expect upgraded code.
I also like that he put the code out there. Even I have a hard time doing that.
Michelle, with all due respect I believe at this point we can safely assume that vast majority of the SAP systems have SALV functionality. As far as ABAP is concerned, this blog could’ve been just mediocre 10 years ago. But it’s 2020 now and it’s time to stop pretending that it’s still OK to code like that.
And while the explanation exists, it’s actually not clear. Our DEV system was down yesterday, so I had time to read this carefully.
Quite honestly, I could just go sentence by sentence and have lots of questions on each one. For example, the button doesn’t just magically appear in ALV toolbar, it requires a special GUI status. This is mentioned nowhere in the blog. How would this be helpful to the beginners?
I understand there must be certain pressure on the Champions to make everyone feel warm and fuzzy on SCN and stuff. But after finding another blog (linked in my other comment) where the author copied the screenshots from and which has the same exact format I suspect this is just the SCN variation of SDN point hunting.
I love debating with you. It's so much fun, because you have valid points. There isn't any pressure on SAP Champions to give warm and fuzzy comments out. Personally I like to encourage people. My comments will be improved by what you have typed. Yes encourage, but also point out the coding technique.
Yes, almost all systems have SALV. So yes, totally agree that it would have made the coding easier. Again you are correct, I did not go line by line through this one and verify everything.
Perhaps this blog is valuable because of the comments that it is generating? I don't know. The screen shots - that's pretty bad, and I didn't comment after you, because I totally agree with your comment. I did "like" it. Totally surprised you even found the other blog. Amazing.
Well there really isn't anything that would be of value for posting any blog - I don't think. I guess you get to go on a list. But I don't know what else would motivate you except your own desire to share and build a good community.
My reasoning and we can agree to disagree:
So is this blog worth posting? Perhaps not. Why? Because you have changed my mind. I really wouldn't want someone to think it was fine to code like this.
Do I wish someone had taken a quick look at it before it was published? Yes, someone could have let him know he was using old coding techniques.
Do I think any coping from other blogs is OK? NO. I had no idea he had copied the screens. Nor did I look at them close enough to notice they were different than the blog. If I had, I would probably have assumed, he just messed up on his screenshots.
In my opinion, it would be better if at least the very first blogs were reviewed by a moderator or someone. SCN is held in higher regard in the community, so the expectations for the content are higher too. (I had quite a bit of prior writing experience before venturing out on SCN.) It's not the best place for the very first blogging experiment IMHO. Just like in skiing, you start with a beginner slope (e.g. a personal blog or internal for a company) and then slowly graduate to more complex ones.
I also hesitate to share code on SCN, it's not hard to notice that none of my blogs are technical. 🙂 But it's mostly because we have so many excellent ABAP bloggers who are much better than me in that area. I do sometimes post code in the answers though.
P.S. It feels like we have material for at least 5 blogs in the comments here already. 🙂
🙂 Yes at least 5. Maybe even more.
Actually, my first blog was here. If I had received a lot of negatives without any positive feedback, it would have been my last.
Yes, there are so many better people than I am. BUT I try ever once and a while to write something. Then if there are better way to do things, I usually find out.
I just looked at CM_FV_PROD_VERS_DB_UPDATE. Never ever use it, it's like direct update to database tables, without any data integrity check!
It is unreleased function module, which is red flag itself (without further looking on code inside).
I have a hard time agreeing that unreleased function modules should not be used. I do really check the code prior to using them. CS_BOM_EXPL_MAT_V2 is an example of a function module that I've used for years. Perhaps I should look for a BAPI. (It's been YEARS since I've used it)
I have other examples as well.
I am not saying it should never be used, but just that it is "red flag".
Something to be aware of and be cautious using these function modules 🙂
So true!!! It just can be frustrating finding what you want and then it not being released. It can drive me a little crazy when looking for things.
And before I use them for the first time I do look at the code, even if it is released to the customer.
Yes. A good blogger would've noted this and provided an explanation why they decided to use it and what was the business case.
I didn't check it. I'm glad someone did.
I thought some of the screenshots looked odd (showing different program name and description) but now I realize they were simply taken from a blog by another author: https://blogs.sap.com/2019/12/30/display-xstring-as-pdf-in-report/
Care to explain how this happened?