Hi SCN,


I’ve been doing some form developments over the past months and I noticed that the standard SAP print / driver programs of the different forms had some differences in them.

I tought it could come in handy to create an overview of what you can include and to explain some parts of the form driver program which will simplify creating your custom Adobe Form driver program.

Note: Before starting to create your custom driver program, inspect the already existing SAP standard driver programs. You might be able to copy and expand or even use already existing driver programs to process your form. Make sure that you use the correct driver program for your Adobe Form since the driver programs for Adobe Forms and Smart Forms have small differences.

The following topics will be covered in this blog:


If you have some remarks or suggestions, feel free to comment so I can improve my blog posts in the future.

Form Processing Flow

First of all it’s important to know the complete flow of the form processing to fully understand the role of the driver program:

  1. You navigate to the transaction of the document you want to print (ex. Billing Document – VF02)

  2. In customizing some requirements (see 2. Form Customizing) are bound towards an output type, if all requirements are met, the output type gets added automatically to the document.

    If you expect that the output type should be added but it’s not, you can always enter the document in change mode (in display mode this can’t be accessed) and navigate towards the output assignment. You can then select “Determination Analysis” from the “Goto” menu and analyze why your output type is not automatically added to the output types of the document.

    The output type can always be added manually to your document.

  3. Issue the the document towards the output type which has been set up in Customizing (see Form Customizing).

  4. The driver program, which is assigned in Customizing towards the output type, will be triggered and all the logic in the program will be executed.
    This logic involves getting the data to correctly process the form (see Importing Parameters / Structures), getting the data of the document (see Gather Document Data), cancel form processing in case of issues (see Issue Handling), update the processing log (see Processing Log), pass the data towards the form and build the layout (see Calling the Form), handle post processing (see Post Processing) and Archive the form when this option has been selected in the Output (see Archiving).
  5. Send the document towards the printer, mail / fax recipient, …

As you can see the driver program plays a big role in the processing of the document so a good driver program will do a lot of work for you.

Form Customizing

The Customizing part of the form is actually out of the scope of this blog but some general knowledge on how the Cusomizing works will help you while creating the driver program. I will therefore give a short explanation what we can set up in Customizing.


Output is generated when several conditions are fulfilled. The basis of the process is the output determination process; based on the document type (ex. billing document) the output determination procedure is selected.


The output determination procedure contains the output types that can be generated as well as requirements that need to be fulfilled for the output to be created. You can maintain procedures in transaction VOFM under Requirements > Output Control. Standard procedures can be used or you can create your own procedure which sends SY-SUBRC = 4 back, if the requirement is not met. If a specific output can be generated according to the procedure, SAP will check if output should be generated for this specific situation.


To determine this, the system will check the user defined settings. These are defined in the condition tables, which are linked through the access sequence. An access sequence is a search strategy that the system uses to find valid data for a particular condition type. It determines the sequence in which the system searches for data. The access sequence consists of one or more accesses. The sequence of the accesses establishes which condition records have priority over others. The accesses tell the system where to look first, second, and so on, until it finds a valid condition record.


Steps that will be done to do the Output configuration:

  1. Creation of Adobe Form + Print program.
  2. Set-up of Output Type: a copy of the standard Output Type can be used as basis for the new Output Type.
  3. Set-up Access Sequence: the access sequence will show the search strategy that the system uses to find valid data for a particular condition type.
  4. Set-up Condition Table.
  5. Assign the Output Type to a Partner Function: in this step you assign the allowed output types to partner functions and specify the allowed type of output processing for the combination of output types and partner functions.
  6. Add output to the Output Determination procedure: in this step the output is added to an output determination procedure. To find the correct procedure some checks will be made.
  7. After the Customizing is done, conditions on the user side will be set up (VV71).


To see how Output Types and their processing routines are set up, you can go to the NACE transaction, highlight the line of the application area of your document and press Output Types. Select the Output Type you wish to investigate and you can see the “Mail title and texts” (sending the form via mail), “Processing routines” (driver program, form routine in driver program that is called, Adobe Form object and type of form) and “Partner functions” (default partner for every medium).

Importing Parameters / Structures

A lot of information about Form Processing has been set up via Customizing as mentioned in the previous chapter.

To be able to influence the Form Processing the data is passed towards our driver program as importing parameters, structures and variables.

Structure NAST: the message details of the Output Type.

Structure TNAPR: the processing routines of the Output Type.

In the Processing Rountines of the Customizing we defined a FORM Rountine which is the starting point in the driver program.

This FORM Rountine is called for starting the form processing and the parameters “return_code” and “us_screen” are passed from a previous SAP program (that initiates the form processing) towards the routine of our driver program:

FORM entry USING return_code us_screen.

When we are facing errors during form processing in the driver program, we can change this “return_code” variable (type sy-subrc) to 1 so that after leaving the driver program, the SAP system will be informed that the form processing resulted in an error. Return 0 when no error was found.

The “us_screen” variable (single character flag) will contain the value ‘X’ (= abap_true = not initial) in case the print is a print preview. In this case no spool job needs to be created and the form does not need to be printed physically.

In “Output & Document Parameters” we dive deeper into influencing the form processing based on the importing structures.

Clear Global Data

Maybe it’s obvious but it’s definately crucial to clear all your global driver program TOP include data at the start of your driver program.

You can create a form rountine that clears your TOP include data to prevent that old data is used which happened to me in the underneath scenario.

Ex.: Your print program is called several times in the same loop and the previous data is buffered into your global data tables.

Gather Document Data

A routine is created to gather all the data which needs to appear on the form.

We try to work with form routines as much as possible since they are easy maintainable, thus making the program well-structured.


The field NAST-OBJKY is the starting point for gathering the data since it holds the document number.


The driver program is the best point to gather your data which needs to appear on the form since the chances are high it can be reused for other forms in the same application area.


Ex: Invoice and Credit Note Header and Item data is stored in tables VBRK and VBRP, so 1 print program can be used for the processing of both forms.


The data can also be gathered in the Interface of the form but the downside of this approach is that the code can’t be reused. You need to take a copy of the interface if you want 2 forms with the same coding, which means you have to maintain 2 forms when parts of the coding changes.


You could resolve this issue by creating an include file that you include in both Form Interfaces, but be aware that this can cause synchronization errors between the generated function module of the form and the include file when one of both changes.

Output & Document Paramaters

A lot of information, that has to influence form processing, is already passed towards the driver program as structures and variables and the remainder data will be gathered based on the already existing data.


The best practice is again to create a new form routine in which we will bind all the external data which we need towards the internal types and gather the additional data based on this.

Underneath you can find some code snippets in which we pass the data towards the internal types, I’ll briefly explain each of the code snippets.

The field NAST-NACHA holds the transmission medium, based on this medium we will have to get different data.

In case of an External Send (can be hard copy, mail or fax), we need the address of the receiver. If this is not present in our global data, after the “Gather Document Data” is done, we need to gather this with help of the Function Module “ADDR_GET_NEXT_COMM_TYPE”. This FM will get the default transmission medium of our customer and in case this is “e-mail”, it will gather the customers e-mail address.
In case we are sending our form via mail or fax we will also have to put the value “cs_outputparams-getpdf” to TRUE.
The structure “cs_outputparams” is needed to open the Spool job for Adobe Forms processing.

CASE nastnacha.
WHEN gc_nachaexternal_send. “= 5

IF NOT nasttcode IS INITIAL.

*       If sending to customer not listed under Partners
IF gs_vbdkradrnr IS INITIAL AND
nastparnr IS NOT INITIAL.

SELECT SINGLE adrnr FROM kna1
INTO gs_vbdkradrnr
WHERE kunnr = nastparnr.
ENDIF.

CALL FUNCTION ‘ADDR_GET_NEXT_COMM_TYPE’
EXPORTING
strategy           = nasttcode
address_type       = gs_vbdkraddress_type
address_number     = gs_vbdkradrnr
person_number      = gs_vbdkradrnp
IMPORTING
comm_type          = lv_comm_type
comm_values        = ls_comm_values
EXCEPTIONS
address_not_exist  = 1
person_not_exist   = 2
no_comm_type_found = 3
internal_error     = 4
parameter_error    = 5
OTHERS             = 6.

IF sysubrc <> 0.
* Error Handling
RETURN.
ENDIF.

CASE lv_comm_type.
WHEN ‘INT’ “e-mail
cs_outputparamsgetpdf = abap_true.
cv_device              = gc_deviceemail. “= ‘E’
gv_email_addr          = ls_comm_valuesadsmtpsmtp_addr.
WHEN ‘FAX’.
cs_outputparamsgetpdf = abap_true.
cv_device              = gc_devicefax.  “= ‘F’
nasttelfx = ls_comm_valuesadfaxfax_number.
nasttland = ls_comm_valuesadfaxcountry.
WHEN ‘LET’. “Printer
cv_device              = gc_deviceprinter. “= ‘P’
ENDCASE.
ELSE.
cv_device = gc_deviceprinter“= ‘P’
ENDIF.

WHEN gc_nachaprinter.
cv_device = gc_deviceprinter“= ‘P’
WHEN gc_nachafax.
cs_outputparamsgetpdf  = abap_true.
cv_device               = gc_devicefax“= ‘F’
ENDCASE.

Sometimes we need to check if it’s the first time we are printing the document, for instance for displaying the text “Original” or “Copy” in the footer of our document. This is necessary because it’s advised to allow only 1 original document print-out.

If the code snippet underneath returns something, it means we already printed the Output Type and we can pass a repeat flag towards our form.

DATA:
lv_nast TYPE nast.

* Get message
SELECT SINGLE * INTO lv_nast FROM nast
WHERE kappl = nastkappl    “#EC *
AND objky = nastobjky
AND kschl = nastkschl
AND spras = nastspras
AND parnr = nastparnr
AND parvw = nastparvw
AND nacha BETWEEN ‘1’ AND ‘4’
AND vstat = ‘1’.


IF sysubrc IS INITIAL.
repeat = abap_true.
ENDIF.

In case of a print preview we don’t need to get the PDF object and we check the preview flag in the output parameters.

IF xscreen = abap_true.
cs_outputparamsgetpdf  = space.
cs_outputparamspreview = abap_true.
ENDIF.

For the language of the form we use the defaulted partner (set up in customizing) language which is stored in NAST-SPRAS.

If this language is not present we can still assign a second fallback language (in this case the Sales Organization language from the billing document header view) and a third language which should be “E” (= English).

* Set Language
cs_docparamslangu      = nastspras.
cs_docparamsreplangu1  = gs_vbdkrspras_vko.
cs_docparamsreplangu2  = gc_english.      “=’E’
cs_docparamscountry    = gs_vbdkrland1.

If we want to use the archiving function of form processing (see title Archiving for more information) we have to include the “toa_dara” structure into the cs_docparams.

* Archiving
APPEND toa_dara TO cs_docparamsdaratab.

The following variables are bound from the imported structures towards internal types which we are using to call our form:

cs_outputparamsnodialog  = abap_true.

     “=> Hide dialog with Output Type options (they can be modified in the document)

cs_outputparamsdest      = nastldest.

     “=> Destination of the print (ex. printer name)
cs_outputparamscopies    = nastanzal.

     “=> Number of prints: if this value is “0” you should default it to “1”

cs_outputparamsdataset   = nastdsnam.

     => Spool Name
cs_outputparamssuffix1   = nastdsuf1.

     => Spool Suffix 1
cs_outputparamssuffix2   = nastdsuf2.

     => Spool Suffix 2
cs_outputparamscover     = nasttdocover.

     => Flag for printing SAP cover page
cs_outputparamscovtitle  = nasttdcovtitle.

     => Spool Description
cs_outputparamsauthority = nasttdautority.

     => Print Authorization object, will be checked in OPEN_JOB when calling the form
cs_outputparamsreceiver  = nasttdreceiver.

     => Spool recipient name
cs_outputparamsdivision  = nasttddivision.

     => Spool department name
cs_outputparamsarcmode   = nasttdarmod.

     => Archiving mode: can be print only, archive only or both
cs_outputparamsreqimm    = nastdimme.

     “=> Print Immediately flag
cs_outputparamsreqdel    = nastdelet.

     => Release After Output flag
cs_outputparamssenddate  = nastvsdat.

     => Request date for sending message
cs_outputparamssendtime  = nastvsura.

     => Request time for sending message (from)

There are a lot other parameters which can be set but I’ve only described the most important ones over here.

For a full overview you can inspect the structure “SFPOUTPUTPARAMS” to see a list of all the possible parameters which you can pass towards the JOB-OPEN Function Module which opens the spool job (See Calling the Form).

Calling the Form

After you have gathered all the data that needs to appear on your form and you have bound all data needed for form processing towards the internal types we can now call the Form.

Calling the Adobe form is different than calling a Smart Form object; To call an Adobe Form you need to open and close the spool job yourself.

First we check if the amount of print-outs is not equal to zero, if this is the case we default it to 1.

IF ls_outputparamscopies EQ 0.
lv_anzal = 1.
ELSE.
lv_anzal = ls_outputparamscopies.
ENDIF.

Then we open the spool job and pass the structure ls_outputparams (which we filled in “Output & Document Paramaters”) as importing parameter.

*   Open the spool job
CALL FUNCTION ‘FP_JOB_OPEN’
CHANGING
ie_outputparams = ls_outputparams
EXCEPTIONS
cancel          = 1
usage_error     = 2
system_error    = 3
internal_error  = 4
OTHERS          = 5.
IF sysubrc <> 0.
cv_retcode = sysubrc.
*     Error Handling
RETURN.
ENDIF.

To call the Adobe Form, we don’t call the Adobe Form object since we can’t execute this object type, therefore we call the unique generated Function Module name of the Adobe Form object. Since the generated FM name can differ from SAP system to SAP system we use the FM “FP_FUNCTION_MODULE_NAME” to get the unique generated FM name on the current system.

* Get the name of the generated function module
TRY.
CALL FUNCTION ‘FP_FUNCTION_MODULE_NAME’
EXPORTING
i_name     = lv_form
IMPORTING
e_funcname = lv_fm_name.

CATCH cx_fp_api_repository
cx_fp_api_usage
cx_fp_api_internal.
cv_retcode = 99.
*       Error Handling
RETURN.
ENDTRY.

We use this generated FM name to call the Adobe Form the amount of times that was declared in the Output Type (passed towards variable “lv_anzal”).

The first time of the loop, the “repeat” fla