1 6 7 8 9 10 62 Previous Next

ABAP Development

930 Posts

After reading  Gungor Ozcelebi's blog, Top 10 ABAP crimes, I though I would share some Wisdom from "The Zen of Python" written by Tim Peters. This is valid for any programming language or developer, especially ABAP . Also check out  Python's PEP - 8 guideline which lays out strict coding formats and standards but the language does not enforce these. This would be a good idea for the ABAP elders to create some general consensuses or rules for programming guidelines.


Copied from https://www.python.org/dev/peps/pep-0020/


The Zen of Python by Tim Peters


    Beautiful is better than ugly.

    Explicit is better than implicit.

    Simple is better than complex.

    Complex is better than complicated.

    Flat is better than nested.

    Sparse is better than dense.

    Readability counts.

    Special cases aren't special enough to break the rules.

    Although practicality beats purity.

    Errors should never pass silently.

    Unless explicitly silenced.

    In the face of ambiguity, refuse the temptation to guess.

    There should be one-- and preferably only one --obvious way to do it.

    Although that way may not be obvious at first unless you're Dutch.

    Now is better than never.

    Although never is often better than *right* now.

    If the implementation is hard to explain, it's a bad idea.

    If the implementation is easy to explain, it may be a good idea.

    Namespaces are one honking great idea -- let's do more of those!

I would also like to point out that we should not be so quick to blame the fellow developers in ABAP but the rather the language. ABAP may be open source but it is proprietary. Many people in ABAP may have never worked with another language and often learn from outdated materials, bad examples, or over priced books. Another consequence of ABAP's proprietary status is the lack of appreciation for the work, especially for contract developers. How many people do you know who develop in ABAP as a hobby or learned it just to make something cool? This may or may not even be some of the reasons SAP is offering an alternative JAVA instance (as if JAVA is a better alternative to anything). I think it is a good idea for any developer in any language to learn other technical skills/languages to get a broader appreciation for development. In any case, ABAP certainly has it's advantages as well.

I just realized how to use CURR type with more than 2 decimals.


I created a DOMAIN with Data Type CURR and 8 decimal places. Look at what happens at SE16N Details:


Value on right (Value Unconverted) is the real values on table with 8 decimals, and values on LEFT are showing on any ALV Output (SE16, SE16N or any ALV report). There is NO Conversion Routine at the Domain, only standard CURR type.



I could set DECIMALS on SALV using CL_SALV_COLUMN->SET_DECIMALS( '8' ), but it will only add ZEROS


Ex for first line in table, TOTFRDOC will show 11,006,361.60000000 instead of 11.00636160.




Yes, you may use CURR type with more than 2 decimals, knowing that this output conversion will happens.

So when use it?? If you want your CURR value converted to percentage (as a rate) usually using 4 decimals. Example:

Value unconverted: 0.6160

Output will show as percentage 61.60

Out of curiosity...

there are 57 standard domains type CURR with more than 2 decimals in my SAP system, 109 standard tables using that without any conversion...but most of them are rates with 4 decimals, so when converting to 2 decimals it shows value multiplied by 100, as in the example above.

I could fix my domain using DataType DEC instead. My ALV outputs are now showing right values with 8 decimals.

The events in the smartforms are helpful in the requirements, where we need the following scenarios to be done:

  1. Page break based on the document(sales order number, delivery number, ship-to number)
  2. To print the subtotal of the smartform.
  3. Page number Reset.


For printing the sub total of the smartforms, the links are already available.

This blog contains the detailed description about how to set the page break, based on the document number and page number reset.


1.1 Steps to be followed in Page Break


The below steps are followed and code is applied for the page break:


  • Sort the table, in which our loopis present on the basis of the field where the page break is to be applied; in this case, it would be “Ship-to” (KUNNR).


  • An automatic event would be created named KUNNR.


  • Right click on the event and create a command. This command would be to print the new ship-to party and its corresponding data in the new page.


The output of the form would be as below:



This would be the first page and the next page would have a different ship to number



2.    How to Reset Page Number


Generally, in smartforms, we would use the system fields for printing the page number. There could be a requirement, wherein the page numbers are to be reset according to the document number. Taking the above given example, in case the page break is in the occurrence of new ship-to number, the page number should reset, rather than the continuous increment of the page number.


Say, for instance, there are 2 ship-to numbers; the first ship-to number has 2 pages of data, so, the page number here should be printed as:

      • Page 1 of 2
      • Page 2 of 2


The second ship-to number has 1 page, so here the page number would be Page 1 of 1. To achieve the above requirement, events in the smartforms would be used. The flow here for page break logic would always go as below:


      • Initialization
      • Header
      • Sort Beginning
      • Sort End
      • Sort Beginning (would go here again for the next new document number)
      • Footer


Sort beginning and end areas are created automatically at the time when the sort is applied at the table. We just need to click on sort begin and sort end. Below is the screenshot for the same:


Following is the code to be executed:


In the initialization, we need to count the total number of pages for each new ship-to, the same variable is saved in the final table (used as in loop).


DATA: lw_count TYPE i,
      lw_tabix TYPE i,
      lst_lips_kna1 TYPE gty_lips_kna1.

gw_page = 1.
LOOP AT gt_lips_kna1 INTO gst_lips_kna1.
  ADD 1 TO lw_tabix.
  AT NEW kunnr.

    lw_tabix = 1.
  IF gw_page IS INITIAL.
    lw_count = 31.   "  It is set 31 here for the number of line items, the value would differ according to the line items one needs to print in a page

   lw_count = ( gw_page * 30 ) + 1."All the line items would be in the multiple of 30 + 1. For eg : 60 + 1, 90 + 1 etc.
  IF lw_tabix >= lw_count.
    ADD 1 TO gw_page.
  CLEAR gw_page_total.
  AT END OF kunnr.
gw_page_total = gw_page.
    gw_page = 1.
  IF gw_page_total IS NOT INITIAL.
    gst_lips_kna1-page_total = gw_page_total.
    MODIFY gt_lips_kna1 FROM gst_lips_kna1
    INDEX sy-tabix TRANSPORTING page_total.
    READ TABLE gt_lips_kna1 INTO lst_lips_kna1
    WITH KEY kunnr = gst_lips_kna1-kunnr.
    IF sy-subrc EQ 0.
      lst_lips_kna1-page_total = gw_page_total.
      MODIFY gt_lips_kna1 FROM lst_lips_kna1
      INDEX sy-tabix TRANSPORTING page_total.

CLEAR gw_page.


lw_count is taken as the counter for the number of line items that would fit in one page.

gw_page_total is the variable to print the total number of pages in one ship-to.

gw_page will store the value for the current page to be printed.


The header portion will have the following code:


      IF gw_new_sort EQ 'X'.
        CLEAR gw_new_sort.
        ADD 1 TO gw_page.


This code sets in the flag, which is used later in the footer part.


Sort begin would have this code to set the current page of the ship-to:


CLEAR gw_count1.
IF gw_count <= 1." This is done to avoid setting the flag as ‘X’ at the first run of the loop

  gw_new_sort = 'X'. " This flag is set to identify the beginning of a new page

READ TABLE gt_lips_kna1 INTO lst_lips_kna1
      WITH KEY kunnr = gst_lips_kna1-kunnr.
IF sy-subrc EQ 0.
  IF sy-tabix = 1 .
  lw_tabix = sy-tabix - 1. “ This marks begin of next document so to fetch the total pages for current ship-to number, subtracted 1 to get last record of current.
    READ TABLE gt_lips_kna1 INTO lst_lips_kna1 INDEX lw_tabix.
  gw_page_total = lst_lips_kna1-page_total.


Sort End:


To fetch the total number of pages of the current document, as per the logic coded in initialization. 





READ TABLE gt_lips_kna1 INTO lst_lips_kna1
      WITH KEY kunnr = gst_lips_kna1-kunnr.
IF sy-subrc EQ 0.
  gw_page_total = lst_lips_kna1-page_total.



The footer would be the area where the page number needs to be printed. After placing the text with the fields of the current page and page total, the code is to be placed in order to reset the page values.



IF gw_new_sort EQ 'X'.
  gw_page = 1.
  READ TABLE gt_lips_kna1 INTO lst_lips_kna1_n
      WITH KEY kunnr = gst_lips_kna1-kunnr.
  IF sy-subrc EQ 0.
    gw_page_total = lst_lips_kna1_n-page_total.

Issue: Create a Report with 2 ALV tables. These should open in full screen and take the maximum amount of space available on the screen. This one is for ABAP beginners.


There is a Report by Ruchi Tiwari which do nearly the same, but use a fixed sized custom control.


  • Create an empty Dynpro with screen number 2000. Create the PBO (status_2000) and PAI (user_command_2000) within forward navigation. I didn’t change the names. You can use includes but is quiet a god idea for real life use, but in this example I’ll put all code in my program “file”.
  • Add a status, because there will be a point in time, where you wish to leave the program( or not )


  • Add the PAI code for the exit. Your PBO and PAi should now look like this.2.png

          Now you should be able to call the programm and leave it with the exit button.

  • Add the code to the Programm:

"Data for output
DATA: gr_container TYPE REF TO cl_gui_docking_container.   "The carrier for the split container
DATA: lv_splitter TYPE REF TO cl_gui_splitter_container"The splitter itself
DATA: lv_parent1 TYPE REF TO cl_gui_container.           "parent 1 and 2
DATA: lv_parent2 TYPE REF TO cl_gui_container.

DATA ref_grid1 TYPE REF TO cl_gui_alv_grid.
DATA ref_grid2 TYPE REF TO cl_gui_alv_grid.
DATA: gr_table1 TYPE REF TO cl_salv_table.
DATA: gr_table2 TYPE REF TO cl_salv_table.

"Some data used for DB query




*&      Module STATUS_2000  OUTPUT
MODULE status_2000 OUTPUT.

"Now we create a docking container which will use the hole screen. So the Dynpro 2000 can't be seen anymore.
CREATE OBJECT gr_container
*     parent                      = g_grid_main
repid                       = sy-repid                                  "needs report id
dynnr                       = sy-dynnr                                  "need dynpro number
side                        = cl_gui_docking_container=>dock_at_bottom  "we want to add the docking on the bottom of the screen 2000
extension                   = cl_gui_docking_container=>ws_maximizebox "The Dockingcontainer should use the hole screen
*     style                       =
*     lifetime                    = lifetime_default
*     caption                     =
*     metric                      = 0
*     ratio                       = 70
*     no_autodef_progid_dynnr     =
*     name                        =
cntl_error                  = 1
cntl_system_error           = 2
create_error                = 3
lifetime_error              = 4
lifetime_dynpro_dynpro_link = 5
OTHERS                      = 6.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.

**   create splitter container in which we'll place the alv table
CREATE OBJECT lv_splitter
parent  = gr_container
rows    = 2
columns = 1
align   = 15. " (splitter fills the hole custom container)
**   get part of splitter container for 1st table
CALL METHOD lv_splitter->get_container
row       = 1
column    = 1
container = lv_parent1.
**   get part of splitter container for 2nd table
CALL METHOD lv_splitter->get_container
row       = 2
column    = 1
container = lv_parent2.

***  Display first ALV
PERFORM set_display.
***  Display second ALV
PERFORM set_display1.

ENDMODULE.                 " STATUS_2000  OUTPUT
*&      Module USER_COMMAND_2000  INPUT
MODULE user_command_2000 INPUT.
IF sy-ucomm = 'EXIT'.
ENDMODULE.                 " USER_COMMAND_2000  INPUT
*&      Form SET_DISPLAY
FORM set_display .
*... Create Instance
CALL METHOD cl_salv_table=>factory
r_container  = lv_parent1
r_salv_table = gr_table1
t_table      = gt_mara.

*... Display table
gr_table1->display( ).

FORM set_display1 .
*... Create Instance
CALL METHOD cl_salv_table=>factory
r_container  = lv_parent2
r_salv_table = gr_table2
t_table      = gt_mard.

*... Display table
gr_table2->display( ).

Gungor Ozcelebi

Top 10 ABAP crimes

Posted by Gungor Ozcelebi Feb 24, 2015

Unfortunately It is not very rare to see sloppy codes in ABAP especially if you are working as a consultant. In this blog  you can see my  selection of worst ten examples. Some might be debatable but some are definitely not. It would  also be nice to discuss examples, cases you experienced in  comments section.


1. Complete program with no modularization blocks

Hundreds sometimes, thousands lines of code with no modularization blocks, no includes. Conditions with hundreds lines of codes. No readability, definitely,  It is my worst nightmare to be asked to correct or change this kind of code. It is not really different having only 2-3 modularization blocks with hundreds line of code.


2. Modification/repairs

Not all of them, but if any modification is done without enough investigation might cause big issues in time and I guess everyone would be agree it is not a good approach to modify standard programs without searching  any alternative solution. So I would suggest to think again if you are often doing modifications. And check first if it is possible to make enhancement instead of modifications.


3- Not considering performance

Codes with nested loops, one big loop with 30 select single, inside, select *, uncontrolled “read”s. They will decrease the performance more than you can imagine and should be avoided all the time. You can avoid some of them using proper design and  many alternative options are in place only if you search them.


4- Development in productive and quality assurance systems

Not very often, but it is not impossible to see such cases, no need to say it may cause big headaches and you are taking big risk if for any reason you are doing this.


5- No success and failure checks

I have seen few of these kind of program, probably it is thought that every single statement in the code will run smoothly but it is  not always the case. and If you  can not see any sy-subrc checks in a program this is not a good sign at all.


6- Not using exceptions

Very similar to point five, it is nice to consider all possibilities and implementing exceptions as described.


7- Copying big templates to every program

Especially for ALV, it is easy to prepare a template with all functionality and copying this template any time where ALV is used.Even it helps to save time, if any function is not needed for the copied programs they should be cleaned, if they are not they will cause issues.


8- Too many global definitions

Global definitions should only be used if they are needed by design, and in a good design they should not be needed too much. 


9- Lack of comments

Readability is very important, and where a complex logic is coded it is very helpful to put some comments to explain what is exactly done.


10-  Too many errors and warnings after SLIN and SCI checks in a recently developed program.

Things might change in time quickly in programming  and easiest way to see if we are on a good track in terms of development quality is  using automated tools like SLIN and SCI. It  is very easy to use, very explanatory and will save us from lots of mistakes. If you are not using this tools you might never know, if something that you have coded is right or wrong. Using these check tools will easily cure most of the mistakes mentioned above. 

It seems ten is not enough I am also adding other points should also have been in this list which are mentioned in comments section

11- Copying standard programs ( From Matthew Billingham)

If you copy standard programs, in every upgrade there will be difference between  copy program and new version of the copied standard which is not a good thing. And it will require big efforts to adapt new functionality into copy program.


12- Writing code like 1999 ( From Patrick Weber )

Using obsolete statements all the time it is very annoying.

13-  Badly formatted source code. ( From Peter Inotai )

It effects readability negatively.

14- Avoid 'Hard codings' (From Tuhin das )

while displaying any text in the program. We should try and use standard text and text elements as much as possible.

15- Using exit and commit work statements in enhancements  (From Johnson Ittyerah ).

The EXIT ensures that any new functionality that you add below doesnt even run because the EXIT is encountered. And the COMMIT WORK leads you on a wild goose chase when you encounter an Update error sometime later.



To keep all the troubles away ABAP programming guidelines  ( from  Horst Keller )



This blog entry stem from this question I asked (https://scn.sap.com/thread/3697966) and the response that I got from Jānis B. I think that all the credits should go to him .


I am working for a chemical company and there is a use of chemical characteristic in data tables (e.g chemical formula,).


For example: H<SB>2</>O boil at 100<SP>0</>


This piece of data needs to be printed like this:


We have some old development that use sap script.

Within SAP script form we can define "Character formats" that will intercept the SB,SP tags:


We are in 2015 so the current technologies are Smart forms and Adobe forms and we would like to use those:


The data definition involve is similar for both technologies , we need to add to our internal table a new "field" , this field is actual a table type tline_tab ,


I created a global class (SE24) to put my data type definitions:



The table values:



Look into the internal table :



Smart form implementation:


Import parameter:



Global:(ignore ST_SFLIGHT)




The table loop:



The use of dynamic text:


We are printing the content of the internal table.



The last ingredient is a Smart Style: (for demonstration purpose I also use some color)

The SP tag is identical but uses the superscript .


To control the height of the table row I use the "Paragraph Format" .



The Smart Style as used in the form:



The result:

Adobe form implementation:

(I am a newbie with adobe form and I got a lot of help from a team member Jenya Hondrash )

Data definition:



Form interface:



Form context:


Form layout:









The result:



Phew that was a long one....



In Standard Scripts you may find data as such variable logo and variable data(variable in the sense, image and data change as per document) are combined in a include text and printed.where as same include text doesn't get printed in smartforms.


Becasue smartforms doesn't have the functionality to execute include texts line by line as we have it in scripts.

In such a case, first you need to separate data using read text(because data may vary according to the document) and print using some internal table.

Identify the logo and upload it in se78 to print it in smartforms using graphics.

In scripts we use protect and end protect to use overlap data with the image.
However in smartform you need to use template and print logo and data in the same row of template, i.e same Output structure


Following is the example illustrating the same :


data is maintained in text as below :



below code extracts data and particular image.


DATA : lw_flag TYPE c.

REFRESH: gt_tline,gt_tline1.

CLEAR: gw_name, gst_tline,lw_flag.

gw_name = '0084568432'.


         id                                  = 'ZH20'
         language                      = 'E'
         name                            gw_name
         object                            = 'VBBK'
         lines                               = gt_tline
         id                                    = 1
         language                        = 2
         name                              = 3
         not_found                       = 4
         object                              = 5
      reference_check                = 6
      wrong_access_to_archive = 7
      OTHERS                            = 8.

if sy-subrc Eq 0.

   LOOP at gt_tline INTO gst_tline.

     if lw_flag IS INITIAL.

     if gst_tline-tdformat EQ '*'.
       APPEND gst_tline to gt_tline1.
       DELETE gt_tline Index 1.
       lw_flag = 'X'.

    if lw_flag = 'X'.
     if gst_tline-tdformat NE '*'.
       if gst_tline-tdline CS 'ZHEX-SQUARE'.
         gw_logo_name = 'Z_SQUARE'.
          DELETE gt_tline Index sy-tabix.
   CONDENSE gw_logo_name.



Loop at header data is data in GT_TLINE1.




and the image looks as below :


Please let me know if any queries . thanks

Ravi Adepu

Archiving a Smartform

Posted by Ravi Adepu Feb 23, 2015

I was working on a smartform associated with an output type where it was required to trigger an output with the following requirements.

  1. Archive the output triggered into an external repository, like doculink
  2. Convert the smartform to PDF
  3. Store the PDF on the SAP UNIX directory
  4. Send the PDF as an attachment in an email and archive the same(Transmission Medium = ‘5’)

I was able to fulfill all the above requirements, except archiving of the outputs which led me to do extensive research on this concept and subsequently found a solution for the same.


In this blog, I would like to share my knowledge on this as I have seen many threads asking for solution on this.


1.       What is archiving?

Archiving is the process of storing data in an external repository which can be reused and viewed as and when needed and which is governed in SAP by the storage mode.

SAP allows outputs to be triggered in three storage modes:

  1. Print only – Printout is issued and no archiving of the output issued
  2. Archive only – The output is only archived
  3. Print and archive – Print out is issued and also output is archived



2.       Why archiving is needed?

Archiving allows us to store the related documents together under one folder with information such as date on which the outputs was triggered.

3.       How to view the archived documents?

Method 1: Transaction code J6NY allows us to see all the archived documents under the required folder for the required output with trigger date.

For example, all vendor POs will be under one folder named Vendor Purchasing Folder. Search the document with PO number.



Method 2: Display Originals in communication method


Under messages, select the output successfully processed (Green indicator) , click on "Communication method" then click on "Displ. originals". You will be redirected to where the document is archived.



4.       Required settings to archive

To archive the document with the output type, required document type has to be maintained under Storage system in NACE settings.


5.       Parameters on which archiving of smartforms depends.

    2. OUTPUT_OPTIONS                



ABAP approach to archive the outputs:



"Structure for Smartform Control Parameters
 DATA : gst_control_param    TYPE ssfctrlop.
 "Set the GETOTF field to blank            
 gst_control_param-getotf    = ''.



"Structure for  Smartform Output Options
 DATA :  gst_output_options           TYPE ssfcompop.   
 "Set the TDARMOD TO 2 ( Archive only) / 3 (Print and Archive)
 gst_output_options-tdarmod = '2'.


 gst_output_options-tdarmod = '3'.





Call the smartform with the below parameters

CALL FUNCTION gw_fm_name
 archive_index           = toa_dara
 archive_parameters      = arc_params
 control_parameters      = gst_control_param
 output_options          = gst_output

That’s all , call the smartform with these parameters set as required,the output triggered will get archived in the repository. But to convert the smartform output to PDF and attach in the email we need to get the OTF of the smartform called which will be achieved by setting the GETOTF field ‘X’ as below.


gst_control_param-getotf    = 'X'. "Get OTF of the smartform

But by doing so it fails to archive the document so to overcome you can call the smartform twice with GETOTF = ‘Blank’ and GETOTF = ‘X’, first one to archive and second one to get otf which can be converted into PDF.


Note : Archiving with Transmission medium as External Send (NAST-NACHA = ‘5’)

When output is triggered with transmission medium as external send, the output fails to archive as the GETOTF field is set to ‘X’. So in such case call the smartform twice as explained above.





  1. To archive, GETOTF = space and TDARMOD = 2 or 3
  2. To get OTF, GETOTF = ‘X’ which can be converted into PDF and can be uploaded onto the SAP UNIX server.

It shouldn't be a difficult thing to agree that pricing is one of the most tricky items to handle, especially when it comes to debugging scenarios that break so randomly.


There was an issue where a custom pricing condition (that calculates the discount based on neighboring items in the order) wasn’t giving expected results. In certain cases it worked, but in some other cases it didn’t. Typical on any custom development right!


The calculation was very straightforward and had no chances of breaking. After much debugging it was seen that the RETTKOMV structure was overwriting the values. After ploughing through plenty of SD forms (eg. http://scn.sap.com/thread/236383) which mentioned about this “notorious-yet-popular” structure , I was still clueless on what the issue was.


For anyone who has debugged the pricing routines, you can imagine the patience that we develop as a part of this. The number of times the routines get called is pretty much close to being called insane, if you consider the good number of pricing conditions configured per line item.


But ABAPers have a way of resolving any issue. Its just a matter of time.


It suddenly struck me that to get the details of the line items, we were referencing a structure from SAPMV45A… and this one kept changing focus depending on the various updates to the line items. Bingo.


Moral of the story – the pricing condition values getting overwritten because of RETTKOMV is a pretty standard scenario. The framework ensures the dependencies are correctly handled and the prices not distorted because of the umpteen pricing conditions. Care should be taken to make use of the right structures (which again, is dependent on the scenario under consideration) to set the price values. And do not make use of specific item level structures from SAPMV45A as they may change amidst the loops! In the worst case scenario, use the whole table content from SAPMV45A (ensuring performance isn’t significantly impacted) and read the required content based on the item number on which the pricing is to be applied.


This was just a tip.. if at all anyone runs into a similar issue.

This blog presents the concept of intelligent pagination which is used for restricting or controlling the jet form layout based on some criteria.

Jet forms:

Jet forms are used to produce electronic templates that can be issued via emails or printed using several printers. The output can be in any form like .PDF, .HTML etc.

Jet forms only produce the layout (the data in that has to be populated by ABAP print program like scripts). The job of combining the template and data is done by Print Agent which is the component of Jet form central server.

Software: Jet forms are developed using a third party software provided by Adobe. The software is Adobe Output Designer.

Using Output Designer, each electronic template that you create consists of two pieces:

  • The modifiable layout called the template design (.IFD) file.
  • The compiled output template (.MDF) file.

Print Agent: Print Agent, the output component of Adobe Central Output Server (Central), uses intelligent merge technology to enable applications to output data, merged with electronic templates.

Types of Templates: Using output designer, you can develop two types of templates. They are,

  • Static Template
  • Dynamic Template

Example of a template (.IFD file):


Static Template:

By static template we mean a template that presents the same text, graphics, and field areas to all recipients all the time. Only the user specific data presented in the field areas differs from one recipient to another. The template does not flex to different amounts of data, nor does it eliminate any elements that are irrelevant to an individual user.

Dynamic Templates:

A dynamic template presents text, graphics, and field areas that flex according to the needs of the recipient. In contrast to a static template, a dynamic template presents only those elements and sections of the template that are relevant to the data requested.

“A dynamic template is moreover combination of several sub forms”.

Most commonly, you use dynamic templates when you need to present repeating data that varies in size according to the recipient. Using the example of a purchase order, a dynamic template would show only the number of detail lines pertinent to that order.

The business requirement will dictate whether a template is static or dynamic. When you wish to present the same data to each user, and when the data does not repeat, then a static template is an appropriate choice. Otherwise, a dynamic template provides greater flexibility for presenting data.

Subforms: A subform is an individual section in a template which consists of a group of objects that are relevant to each other.


For example, consider the below template. Here all the item details are grouped together into a subform.


JFPreamble and JFPreamble_1:

The first time you compile a dynamic template, Output Designer creates two custom properties for the template called JFPREAMBLE and JFPREAMBLE_1. These two custom properties, known as DOCVARs (document variables), contain the processing rules for the template. It is JFPREAMBLE that the output Agents look for when the time comes to merge data with template.

Intelligent Pagination:

The output designer gives the flexibility for creating events in the template and making the events active at appropriate time.

Such events can include appropriate page breaking by calculating whether there is adequate space to lay down a subform on the current page or whether to place it at the top of the next page or if there is a need of restricting the pages based on some criteria.

For example, suppose I have 5 pages in my template, but as per the requirement, I have to skip page 1 and print from page 2 to 5 for particular country. This type of requirement can be fulfilled using the Intelligent Pagination concept.

Intelligent Pagination uses several commands to trigger the events and take appropriate action.

We will get a clear picture on Intelligent Pagination concept by understanding the following requirement.

Requirement: Adding a new page after page 1 in already existing layout form and calling the new page instead of page 1 only for specific country. 

Changes to be made in 2 areas:

1) Layout/Template level

2) ABAP Print Program

Template level change:

Template prior to change: Before making the changes, the form template (IFD file) had 3 pages.


Now adding a new page after page 1.



New page inserted as page 2. In this page, copied the page 1 components. We can copy the components from one page to another by CTRL+A and CTRL+C.


Now, there comes the main task, what command need to be used and where it needs to be written.

JFPreamble, is the document variable where we can make our custom changes.



Point to be noticed: While copying the components from page 1 to new page (page2), the names assigned to components in page 2 will be same as in page 1. Hence there will not be any differentiation between page 1 and 2.

As we need to trigger page 2, the idea was basically to change the field name in page 2(new page) and then use intelligent pagination command  !FldUsed on the changed field.

groupname!FldUsed (FieldUsed): This event is triggered whenever the field name mentioned is available with some data.

Hence, we change the name of a global field InvoiceNumber to InvoiceNumber2 in our new page. In page 1, it remains same InvoiceNumber.



Then the box is grouped with group name GInvoice2 as shown below.


Grouping is required because the group dictionary stores all these events.

The basic Syntax:

^define group:<groupname>!<event>        \action

Then, the event !FLDUSED is added in the JFPreamble, as shown below.


The flow of the above 2 highlighted lines goes like,

  • ^define group:InvoiceNumber2!FldUsed        \groupGInvoice2\fieldInvoiceNumber2.

         Here, the print agents triggers this event whenever the field mentioned (InvoiceNumber2) has data in it. Once it is triggered, it goes                      to action that is groupGInvoice2.


  • ^define group:GInvoice2 \page2.

         When the control comes to group GInvoice2, it triggers this event and performs the action \page2, which means goes to page2.


Now here, the changes at template level are done. After making the changes the .IFD file is compiled and .MDF file is generated, which need to be uploaded in the client’s UNIX server.

ABAP Print program changes:

SAP scripts are generally used to produce data stream for jet form layouts. Hence we made changes in the script for restricting the page.

JOB command: This is the first command executed in the script with which the Print Agent is evoked. Hence we made a change for controlling the pages in JOB command as shown below.

“We use certain print agents commands in script level in order to control the printing. The events which we used in template level cannot be used here”.

The below changes are made in JOB command.

If country = specific country.

^JOB &FORM-JOB&_EMAIL -z&template_name &_EMAIL -c001 -aip02  –aspPDF

*Pass the data for InvoiceNumber2.

^GLOBAL InvoiceNumber2



^JOB &FORM-JOB&_EMAIL -z&template_name &_EMAIL -c001  –aspPDF

*Pass the data for InvoiceNumber.

^GLOBAL InvoiceNumber



Few Print Agent Commands:

-aipn -> used for starting the print from a specific page. Hence we specified here –aip02, which starts the print from page 02.

-c   -> it specifies the number of copies to be printed/emailed.

-asp -> Specifies the type of output to be produced, in this case it is PDF.

There might be a confusion, that why do we need to change again in script level when the changes are made in template level.

It is because, in jet form template the first page is the default page and it will be automatically printed. Hence while start of the print agent itself, we need to make sure that we start the printing with page 2.

After making all these changes, observed the output with the new page and the page 1 was not displayed.

There are lot many other Intelligent Pagination commands, I will be posting soon in my next blog with suitable examples. Thank you for reading.

Many times we are required to create text symbols for hardcoded texts, reason being that with text symbols translation becomes possible and then the same program can be used across different regions/countries with other languages.


Depending upon the number of the hardcoded text one has in his/her program text symbol creation can become either an easy or a difficult job. To create text symbol one can simply double  click the hardcoded text and it will ask if you want to create a text symbol, then you move to the text symbol screen where you save and return back.


As a part of a CI initiative my company we managed to automate this process. We simply execute a program (source code provided below) and provide it with the 'PROGRAM NAME' of which we want to create text symbol and that’s it! The program create the text symbols where ever its necessary.


The information on which text is required for translation is picked up from extended code check program. Also if you have yourself given a number to a hardcoded text and forgot to create a text symbol for it, the program would do it for you. If you already have some text symbols created and are adding new ones then the next number is picked up by the program.


Using this program we were able to cut down on the development time. When I am coding I do not worry if I have to create a text symbol for the text and surely save some CLICKS.


One can modify the code as per his/her requirement. Also if you have any suggestions or improvement please do let me know.


This blog gives you a brief overview about Jet forms used in SAP through a third-party software and followed with an example on it.

I do accept Jet forms is an old concept. But, when you get any objective on this and when any client you are working with have a requirement on it, I hope this document will be helpful. Because, I underwent this kind of situation where the client I am working with gave his business requirement on Jet forms. I searched everywhere in SCN and Google but, the information I gathered was quite insufficient and I have striven in delivering the object.

So I am writing this blog for all who are new to Jet forms and if they get any business requirement on this, I am sure that this blog will definitely help.


Jet Form Definition:

Organizations require e-document presentment solutions that enable them to output documents from a single system, in a variety of formats for delivery via multiple channels including mail, e-mail, fax, and the Web.

With an effective end-to-end solution delivery, organizations can also offer customers flexible alternatives for receiving information. An organization can automatically send information to its customers, and at the same time, its customers can actively seek out up-to-the-minute information.

This can be achieved through Jet forms-the times when, smart forms/adobe forms weren't existing.

There is a misconception that, Jet forms are supplants of Scripts in SAP. Jet forms are completely different from SAP Scripts.


How does a jet form synced with SAP Scripts:

From SAP the data comes out in the form of RDI (Raw Data Interface), the sap scripts have to be set to print to RDI. This is then sent to a specific server set on the sap script, with details of the end printer. Jet form then processes this data into a more reasonable format, and then processes this against the jet form.

The result is then sent to the printer.


Pros and Cons:

Jet Form Design is a full “What You See Is What You Get” (WYSIWYG) program. As you draw a form design, you can see it take shape on the screen. All the features, such as text, fields, lines, boxes and logos, and their attributes, such as fonts and shading, appear on-screen as they will print on the target printer.

In Jet Forms, you can incorporate graphical objects, such as lines, boxes, circles and arcs. You can choose attributes such as line widths, line styles, and shading patterns. The product supports the most popular graphic formats for logos, such as .BMP, .PCX, .TIF and .WMF. As well, you can include bar codes printed in many of the common formats.

You can establish a tabbing sequence that specifies the order in which Filler prompts for data entry. You can designate fields as mandatory, or as protected, where the user can see the data values but cannot modify them.

Alongside, there are few disadvantages with them as they cannot be tested with our design before sending it to SAP system which is a main drawback.

After the existence of smart forms/adobe forms, jet forms became obsolete as it could not meet many of the requirements and features that the present forms are providing.

Also as the jet forms are designed by a third-party software, they became the least opted choice.


Software Used:

Jet forms are developed by a third-party software which is provided by Adobe. The software is named as “Adobe Output Designer”.




Step-by-step Process of creating a Jet form and generating a pdf:

Jet form is created in .IFD format and compiled to .MDF (cannot open; raw format) format which has to be uploaded to UNIX server of the client.

Now, we will see how to create a simple jet form and generate its pdf file through a script in sap.


We shall create a jet form design for the below pdf:




In the above pdf we can see, there is a Title field, Date box field, and three box fields for demo purpose.

Now we shall design a Jet form which meets the above sample requirement through Adobe Output designer software.



After installing the software – Adobe Output Designer, you will see a blank screen as shown below after opening it.




Now, we can create a design on the blank page with which ever fields we require. Select the field box in the Toolbox and click on the page.






Double click on the box and you can use options like field name, width, height, font alignment, etc. according to your requirement.







As shown above we can design a page which consists of few field boxes of the required fields and logo if we need. To know more about the tools provided and used in the jet forms, we can place the cursor on the particular box and see why it is used for.




Like this, we can create three field boxes and place them accordingly on the page.




We can utilize the outline box tool to highlight the field boxes if needed.




Now, we create various field boxes for Invoice number, Date, Total amount, etc. and arrange them in an order.




As shown above, If we are using a particular field for Invoice number (in the case of invoice pdf), we have to name it as Invoice Number as shown above. Same with Date field, Amount field and whichever field we are using.


After arranging the field boxes in an order, we highlight all of them as discussed earlier.




We can even create a Title for our pdf by placing few more data field boxes on the top of the page.




We can place a logo of the respective company by clicking on Logo tool and placing it wherever we need. In this scenario, I am placing a logo on the top left corner of the page.







Select OK and place the Logo in the required place on the page.





And now we have completed our first design in Jet Forms. Press the Save 17.png  button and you can test present your design.


Go to menu options -> File -> Presentment targets. Here, select the default printer as PDF, so that you can view the test pdf of your design.




Now go to menu options -> File -> Test Presentment.




The design which you have created will be generated in the form of PDF as shown below:




You have successfully created your first Jet form.


As the SAP system cannot read .IFD format (the format your design is in), we have to compile the Jet form and convert it to .MDF format. As I earlier told you - “From SAP the data comes out in the form of RDI (Raw Data Interface), the sap scripts have to be set to print to RDI. This is then sent to a specific server set on the sap script, with details of the end printer. Jet form then processes this data into a more reasonable format, and then processes this against the jet form.

The result is then sent to the printer.”


The .MDF file which you have generated will be uploaded by BASIS team in the clients UNIX server.


The .MDF file is generated as shown:


Go to menu options -> File -> Presentment Targets and select the list of printers as per your client requirement and click OK.




Now go to File -> Compile and give the path where you want to place the .MDF file on your system.




In colossal designs where a jet form contains 2 or more pages, there is a necessity of “Preamble”, in which we have to write few commands and mention the field names. In few cases, the preamble file will be generated automatically once we compile the design to .MDF format.


You can see the preamble file in menu options-> Format -> Template Preamble





Many of us might have this doubt whether, if the preamble file is created automatically by Jet Form design or whether it’s created manually?

Well, it’s half and half. As long as you have a newer version of Output Designer, it will create the JFPREAMBLE_1 (and _2, _3 and so on as needed) and also make a stub JFPREAMBLE for you.

If you want to change the way it’s doing things, you need to edit the JFPREAMBLE. You can’t edit the others as they will be overwritten every time you recompile the form. Making edits in the JFPREAMBLE, you should put them *after* the ^FILE lines that pull in the other preambles, so that they override whatever might have been in those. Often you’ll find yourself copying lines from JFPREAMBLE_1 to JFPREAMBLE and making only minor changes.


This blog will be continued and I will post again on How to associate the Jet Forms with SAP Scripts in a short period.

As an ABAP Consultant, one thing that you hear and discover from Day 1 is that it is always good to use standard objects. But, there are a lot of times that we feel that the standard does not suit our requirements.


Consider a scenario for a Smartform for an SO Output. We may need to access just say 10 fields of VBAP table, but due to the restriction of smartforms that the srtucutres or tables should be DDIC structures, we either end up using the entire structure of VBAP or create a Z structure. This may not only impede the performance of the program but may also lead to compatibility issues in the future.


Here is where SAP helps us out with the Repository Information System (Transaction SE84). SE84 is a powerful transaction to display data dictionary objects and development properties.

In this blog, I will outline a few uses that I have encountered with SE84 which has helped me to greatly reduce my development times and to use standard objects as opposed to Z objects

1. Structures


Structures are one of the most used objects in SAP. Everything from passing data through interfaces to the creation of internal tables can use structures. So I believe this to be a very important point where we can, or I would say, must use standard objects to the extent possible.


To find a structure meeting your requirements using SE84,

Let’s consider a scenario where you need the data elements VBELN and EBELN together in one structure.

Just go to SE84 and enter the fields as below in the selection screen.




On execution, the transaction returns us with all the structures that meet our requirements.




Similarly, we can also find out other DDIC structures like Tables, Table Types, Views Domain etc.

This tool is especially useful in finding standard data elements meeting our requirements when we need to create Z tables or structures.

For example, if we need to find a data element of type CURR length 13.



2. Message Class


As it is said regarding any application, it should be highly interactive. This interaction relies largely on the messages that we display on the screen.

Most of the times, we either just hard code the message in the program or we create a new message class to suit our needs.


Using this transaction, we can find out the existing messages to suit our needs.


For Example, if we need to find a message to show something like “Material Not Found”,


Just enter something like below in the selection screen.




The transaction will then return all the applicable messages to suit our needs.



3. GET / SET Parameters


Many a times a requirement arises where we need to use the parameter ids of variables, or we need to know all the relevant parameter ids for a particular type of field, say “Customer”


At such times, we can take help of this tool to get a list of all the parameters that we can use to get the relevant parameter IDs.


For getting all the relevant parameter IDs, we can enter the required search term in the selection screen as below.




On execution, we will get a list of all the relevant parameter IDs that we can use.




An important advantage of the transaction SE84 is that as the screen is consolidated with all the objects that we can possibly use, it becomes a one stop shop for all our standard needs.

I would not recommend using this T code for finding out enhancement spots. Don’t get me wrong, it is a great utility. But as far as enhancement is concerned, it is my experience to always find the best enhancement spots in debug mode or through code walkthroughs. This not only helps you understand the flow and decide on the best enhancement spots, but will also help you make sure that the changes you make are as desired and does not get changed again by the SAP Standard process flow.


Hope this short tip helps you in your future assignments.

related page 1
related page 2
related page 3

NOTE: Before beginning, the XLSX Workbench functionality must be available in the your system.


Suppose we need to generate Excel-file in the background mode.

For ease of example, lets create form, that contains only the classical phrase "Hello world !" nested in the rectangte area. The resultant Excel-file we will send via SAP-mail (in this case - to themselves).




As you can see, most of the code takes the mailing (does not apply to the form creation) :


REPORT  z_hello_world .


* declare and fill context

DATA gs_context TYPE lvc_s_tabl .

DATA gv_document_rawdata  TYPE mime_data .

gs_context-value = 'Hello world!' .


* call the form



     iv_formname    = 'HELLO_WORLD'

     iv_context_ref = gs_context

     iv_viewer_suppress  = 'X'


     ev_document_rawdata = gv_document_rawdata


     OTHERS         = 2 .

IF sy-subrc NE 0 .

   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno

           WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 .



* mailing

PERFORM send_mail USING gv_document_rawdata .



*&      Form  send_mail


FORM send_mail USING pv_document_rawdata TYPE mime_data .


     lv_attachment_size  TYPE sood-objlen ,

     lv_subject          TYPE so_obj_des ,

     lv_document_size    TYPE i ,

     lt_document_table   TYPE solix_tab .


     lr_send_request     TYPE REF TO cl_bcs ,

     lr_mail_message     TYPE REF TO cl_document_bcs ,

     lr_recipient        TYPE REF TO if_recipient_bcs ,

     lr_error            TYPE REF TO i_oi_error ,

     ls_retcode          TYPE soi_ret_string ,

     lv_attachment_type  TYPE soodk-objtp VALUE 'XLS' .




       buffer        = pv_document_rawdata


       output_length = lv_document_size


       binary_tab    = lt_document_table.


   lr_send_request = cl_bcs=>create_persistent( ) .


   lv_subject = 'test mail' .

   lr_mail_message = cl_document_bcs=>create_document(

       i_type      = 'RAW'

       i_subject   = lv_subject ) .


   lv_attachment_size = lv_document_size .

   TRY .


           i_attachment_type     = lv_attachment_type

           i_attachment_subject  = space

           i_attachment_size     = lv_attachment_size

           i_att_content_hex     = lt_document_table ) .

     CATCH cx_document_bcs .


   lr_send_request->set_document( lr_mail_message ) .


   lr_recipient = cl_sapuser_bcs=>create( sy-uname ).


   lr_send_request->set_send_immediately( abap_on ) .



       i_recipient = lr_recipient

       i_express   = abap_on ) .


   lr_send_request->send( i_with_error_screen = abap_on ) .



ENDFORM .                    "send_mail



2.1 Launch XLSX Workbench, and in the popup window specify a form name HELLO_WORLD , and then press the button «Process»:





Empty form will be displayed:



2.2 Push button444_19_2.PNGto save the form.



2.3 Assign context LVC_S_TABL to the form:



Herewith, you will be prompted to create a form's structure automatically (based on context):


Let's press the button: .


As result,  «Pattern» () and «Value» () will be added under the «Sheet» in the form structure tree :




Added components will already have a binding with context. For this components, only template binding is required.

We'll do it later, but first we perform markup of template.




2.4 Make markup in the Excel template:




2.5 Template binding:

Assign «Pattern» to a target area in the Excel-template; For assigning, You have to perform next steps successively:


  • Pose cursor on the node in the form's structure tree;
  • Select a cell range [A1 : C3] in the Excel-template;
  • Press a button located in the item «Area in the template» of the Properties tab:





Similary, assign «Value» to a target area in the Excel-template; For assigning, You have to perform next steps successively:
  • Pose cursor on the node in the form's structure tree;
  • Select a cell range [B2] in the Excel-template;
  • Press a button located in the item «Area in the template» of the Properties tab:




Scheme of bindings:


2.6 Activate form by pressing button444_30.PNG.




Launch SE38 and run your report Z_HELLO_WORLD in background mode :









It is a well known fact, that you release the memory occupied by an internal table using either CLEAR or FREE, where FREE releases also the initial memory area. You normally use CLEAR, if you want to reuse the table and you use FREE, if you  really want to get rid of it and don't want to refill it later on. Assigning an initial internal table to a filled internal table does also release the target table's memory in the same way as CLEAR does.


But last week a colleague pointed out to me that it is not such a well known fact that deleting lines of internal tables with DELETE normally does not release the memory occupied by the deleted lines. Instead, there seem to be people deleting lines of internal tables in order to release memory. Therefore as a rule:


Deleting lines of an internal table using the DELETE stament does not release the table's memory.


For an internal table that was filled and where all lines are deleted using the DELETE statement the predicate IS INITIAL in fact is true. But the internal table is only initial regarding the number of lines but not regarding the memory occupied. You can check that easily using the memory analysis tools of the ABAP debugger.


So far so good. For releasing the memory of an internal table you use CLEAR or FREE and you do not simply DELETE all lines.


But what about the use case, where you want to delete almost all lines from a big internal table and to keep the rest? After deleting, the internal table occupies much more memory than needed for its actual lines. If memory consumption is critical, you might want to get rid of the superfluous memory occupied by such an internal table. How to do that?


Spontaneous idea:


DELETE almost_all_lines_of_itab.

DATA buffer_tab LIKE itab.
buffer_tab = itab.
CLEAR itab.
itab =  buffer_tab.
CLEAR buffer_tab.


Bad idea! Check it in the ABAP Debugger. Due to table sharing, after assigning itab to buffer_tab, buffer_tab is pointing to the same memory area as itab. Assigning buffer_tab back to itab after clearing itab is simply an effectles roundtrip and you gain nothing.


Improved idea:


DELETE almost_all_lines_of_itab.

DATA buffer_tab LIKE itab.
buffer_tab = VALUE #( ( LINES OF itab ) ).
CLEAR itab.
itab =  buffer_tab.
CLEAR buffer_tab.


Now it works! Instead of copying itab to buffer_tab you can transfer the lines of itab sequentially to the initial target table and the memory is not shared. Before 7.40, SP08 you have to use INSERT LINES OF itab INTO TABLE buffer_tab instead of the VALUE expression, of course.


What also works for the use case is:


DELETE almost_all_lines_of_itab.

DATA buffer_tab LIKE itab.
buffer_tab = itab.
INSERT dummy_line INTO TABLE buffer_tab.
DELETE buffer_tab WHERE table_line = dummy_line.
CLEAR itab.
itab =  buffer_tab.
CLEAR buffer_tab.


By inserting a dummy line into buffer_tab and deleting it again, the table sharing is canceled and buffer_tab is built from scratch (but only, if it needs considerably less memory than before; otherwise it is copied and nothing is gained again).


Ingenious minds might also find the following ways:


DELETE almost_all_lines_of_itab.

DATA buffer_string TYPE xstring.
EXPORT itab TO DATA BUFFER buffer_string.
CLEAR itab.
IMPORT itab FROM DATA BUFFER buffer_string.
CLEAR buffer_string.


or even


DELETE almost_all_lines_of_itab.

                       RESULT XML DATA(buffer_string).
CLEAR itab.
                       RESULT itab = itab.

CLEAR buffer_string.


Yes, those work too, but put some GET RUN TIME FIELD statements around them to see that those are not the best ideas ...


Filter Blog

By author:
By date:
By tag: