SmartForms – Print programs

In order to print a SAP Scrip, Smart Form or Adobe Form you need a print program and an entry point into that program. The entry point is actually a form that gets the data passed in a standardized manner by the calling program. All calling programs use the same way to pass data to the print programs, i.e. in the global variables of the print program. If you need to find out how your caller is passing the data to the print program, take a look into the standard print program.

After the data is passed, the form will read it from the global variables defined in the print program, gather the rest of the data by various means (FM calls, selects and so on), and then call a function module that is automatically generated by the Smart Form when activated. The data is also passed in a standard manner from the form in the print program to the FM generated by the Smart Form, together with control data and user preferences that will instruct the generated FM’s behavior. The generated FM will then actually print, send data via EDI or send it as an email, depending on the instructions received. The communication with the generated FMs is also standardized.

The print program

A print program can be a standard report (executable) that contains forms that can be called externally by programs that need to have their data printed. Only the form will be executed, not the whole program. The program can be also be non executable, but in order to test it easily, an executable is preferred (type 1).

In our example we need to print and/or send via email a form’s output.

The print program’s form can process the data received from the calling program in order to gather other data that needs to be printed out. The data from the calling program is received, in our case, in structures and tables defined in the standard include RVADTABL, so we used the same one in our ***TOP include of the print program. This standard include looks like this:

***INCLUDE RVADTABL .
TABLES: NAST, "Messages
        *NAST, "Messages
        TNAPR, "Programs & Forms
        ITCPO, "Communicationarea for Spool
        ARC_PARAMS, "Archive parameters
        TOA_DARA, "Archive parameters
        ADDR_KEY. "Adressnumber for ADDRESS

TYPE-POOLS SZADR.

*************************Start of Note changes 1508474*********************************

DATA: gv_inupd TYPE i,
        gv_fp_outputparams type sfpoutputparams. " Output parameters..

*External Send
DATA: gv_comm_type TYPE ad_comm,
        gv_comm_values TYPE szadr_comm_values,
        gs_recipient TYPE swotobjid,
        gs_sender TYPE swotobjid,
        gs_intnast TYPE snast,
        gv_xdevice(10),
        gv_xprogramm TYPE tdprogram,
        gv_xdialog.

* sending output vai mail
DATA: gv_pdf_content TYPE solix_tab.

*************************End of Note changes 1508474*********************************

The ***TOP include file will reference this include file and contain also the rest of the global data declarations that will be used for the actual functionality, i.e. selecting the data and sending it to the form in order to be printed).

*&---------------------------------------------------------------------*
*& Report ZMM_POPRINT
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*

INCLUDE ZMM_POPRINT_TOP.    " Global Data
INCLUDE ZMM_POPRINT_F01.
INCLUDE ZMM_POPRINT_F02.

In order to test our functionality we needed the input parameter passed by the calling program in the NAST-OBKJY that contains in our case the Process Order Number and the name of the form that was set in the customizing passed by the same program in the TNAPR-SFORM variable. Note that the name of the form will be later used to find the actual function module name to be called, because every time you activate a form the name of the FM might be changed. Also if you transport the objects in another system the FM name can also changed.

*&---------------------------------------------------------------------*
*& Include ZMM_POPRINT_TOP                          - Report ZMM_POPRINT
*&---------------------------------------------------------------------*
REPORT ZMM_POPRINT.

* Default printing interface
INCLUDE rvadtabl.

DATA: retcode LIKE sy-subrc.
DATA: xscreen(1) TYPE c.           "Output on printer or screen

* set a default PO for testing here
PARAMETERS: pa_docnr TYPE ekpo-ebeln DEFAULT '4500000256',
            pa_email AS CHECKBOX DEFAULT 'X'.

* when running the report, the parameters will overwrite the form structure input
nast-objky = pa_docnr.
tnapr-sform = 'ZMM_PO'.

* this code is executed only by running the report
IF pa_email = 'X'.
  PERFORM entry_email USING retcode
                            xscreen.
ELSE.
  PERFORM entry USING retcode
                      xscreen.
ENDIF.

In our case the form entry only prints the Smart Form (it is not listed here because it only has a call to the smartform_print form)  and the entry_email form can print and also send an email depending on the buttons pressed by the user prior to the call. The smartform_email form calls another form to receive the email address where the email should be sent. Both of this forms (entry and entry_email) are the actual entry points that have to be set in the configuration of the calling program (in our case the calling program is the transaction ME23N). This should be known by the functional consultant and he/she should set the program name, the form name and the Smart Form name to be used.

FORM entry_email USING return_code TYPE sysubrc
                 us_screen   TYPE c.

  CLEAR retcode.
  return_code = 0.
  xscreen = us_screen.

  " Check what is the return code of the pressed button. 
  " In case of the print preview, one of these values might come
  IF SY-UCOMM = 'VIEW' OR SY-UCOMM = 'PREVOUTPUT' OR SY-UCOMM = 'PREV' OR SY-UCOMM = 'PRINT'.
    " call to print OR PREVIEW
    PERFORM smartform_print.
  ELSEIF SY-UCOMM is INITIAL.
    " send the form on email now
    PERFORM smartform_email.
  ENDIF.
ENDFORM.

Passing the data to the Smart Form

The Smart Form generates a Function Module when activated. That FM will be called with some standard parameters that all FMs of this kind have and custom defined parameters defined by the developer when designing the Smart Form.
Before calling the generated FM we first need to find its name because SAP in its wisdom decided that the name of the function module sould be random. So we need to use the function SSF_FUNCTION_MODULE_NAME to find the actual name of the Smart Form’s generated FM. After we have the name we can call it. In our case the real name of the generated FM is (at one point) /1BCDWB/SF00000006.

FORM smartform_email.
  DATA: xebeln       LIKE ekpo-ebeln.

  " data for form print
  DATA: lv_form TYPE tdsfname,
        fm_name TYPE rs38l_fnam.

  " data for email control
  DATA: control_parameters  TYPE ssfctrlop,
        output_options      TYPE ssfcompop,
        email_subject(50)   TYPE c,
        email_po_number(10) TYPE c.

  DATA: email_recipient TYPE swotobjid,
        email_sender    TYPE swotobjid,
        g_mail_app_obj  TYPE swotobjid. " dummy work area for email function

  DATA: is_vendor_adrc TYPE adrc,
        is_vendor_adr6 TYPE adr6.

  " get PO number
  xebeln = nast-objky.
  email_po_number = xebeln.

  " SmartForm name from customizing table TNAPR
  lv_form = tnapr-sform.

  PERFORM get_vendor_details
      USING     xebeln
      CHANGING  is_vendor_adrc
                is_vendor_adr6.

  " build the email objects
  PERFORM mail_recipient_object USING is_vendor_adr6-smtp_addr CHANGING email_recipient.
  PERFORM mail_sender_object CHANGING email_sender.

  control_parameters-device = 'MAIL'.
  control_parameters-no_dialog = abap_true.
  control_parameters-preview = abap_false.

  CONCATENATE 'PO No. ' email_po_number INTO email_subject RESPECTING BLANKS.
  output_options-tdtitle = email_subject.
  output_options-tdnoprint = abap_false.
  output_options-tdnoprev = abap_false.

* find the generated FM name
  CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
    EXPORTING
      formname           = lv_form
*     variant            = ' '
*     direct_call        = ' '
    IMPORTING
      fm_name            = fm_name
    EXCEPTIONS
      no_form            = 1
      no_function_module = 2
      OTHERS             = 3.

  IF sy-subrc <> 0.
*   error handling
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    EXIT.
  ENDIF.

* now call the generated function module
  CALL FUNCTION fm_name
    EXPORTING
* BEGIN of standard parameters
*     archive_index      =
*     archive_parameters =
      control_parameters = control_parameters
      mail_appl_obj      = g_mail_app_obj
      mail_recipient     = email_recipient
      mail_sender        = email_sender
      output_options     = output_options
      user_settings      = 'X'
* END of standard parameters
* BEGIN of customer designed parameters
      iv_ebeln           = xebeln
* END of customer designed parameters
* BEGIN of returning standard parameters
*      importing  document_output_info =
*     job_output_info    =
*     job_output_options =
* END of returning standard parameters
    EXCEPTIONS
      formatting_error   = 1
      internal_error     = 2
      send_error         = 3
      user_canceled      = 4
      OTHERS             = 5.

  IF sy-subrc <> 0.
*   error handling
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ENDFORM.

The function call to the generated FM has some interesting control parameters that will instruct the function to send an email, send to a printer or return the printed document in OTF format in one of the returning parameters. You can find out more if you check this function out.
In the case of the form smartform_print we do not pass the email and control related parameters because the generated FM is set to generate a print as default.

FORM smartform_print." CHANGING job_output TYPE ssfcrescl.
  DATA lv_form   TYPE tdsfname.
  DATA fm_name  TYPE rs38l_fnam.

  DATA xebeln      LIKE ekpo-ebeln.

* SmartForm from customizing table TNAPR
  lv_form = tnapr-sform.

* print data
  CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
    EXPORTING
      formname           = lv_form
*     variant            = ' '
*     direct_call        = ' '
    IMPORTING
      fm_name            = fm_name
    EXCEPTIONS
      no_form            = 1
      no_function_module = 2
      OTHERS             = 3.

  IF sy-subrc <> 0.
*   error handling
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    EXIT.
  ENDIF.

  xebeln = nast-objky.
* now call the generated function module
  CALL FUNCTION fm_name
    EXPORTING
*     archive_index    =
*     archive_parameters =
*     control_parameters = control_parameters
*     mail_appl_obj    =
*     mail_recipient   =
*     mail_sender      =
*     output_options   =
*     user_settings    = 'X'
      iv_ebeln         = xebeln " only send the document number to the Smart Form

*    IMPORTING
*     document_output_info =
*     job_output_info  = job_output
*     job_output_options =
    EXCEPTIONS
      formatting_error = 1
      internal_error   = 2
      send_error       = 3
      user_canceled    = 4
      OTHERS           = 5.

  IF sy-subrc <> 0.
*   error handling
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ENDFORM.

The email source and destination are build as follows:

FORM mail_recipient_object USING email_address TYPE ad_smtpadr
  CHANGING email_recipient.

  CALL FUNCTION 'CREATE_RECIPIENT_OBJ_PPF'
    EXPORTING
*IP_COUNTRY =
*IP_FAXNO =
      ip_mailaddr       = email_address
      ip_type_id        = 'U'
    IMPORTING
      ep_recipient_id   = email_recipient
*EP_ADDRESS =
*ET_RECIPIENT =
    EXCEPTIONS
      invalid_recipient = 1
      OTHERS            = 2.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ENDFORM. " mail_recipient_object

FORM mail_sender_object CHANGING email_sender.

  CALL FUNCTION 'CREATE_SENDER_OBJECT_PPF'
    EXPORTING
      ip_sender      = sy-uname
    IMPORTING
      ep_sender_id   = email_sender
    EXCEPTIONS
      invalid_sender = 1
      OTHERS         = 2.

  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
    WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.
ENDFORM. " mail_sender_object

References: