In this example, we’ll show you how to display a static HTML invoice with dynamic content using a Universal Display File. A Universal Display File is a display file that an RPG program can use to create dynamic text output. The output can be transmitted to a client via HTTP or written to a stream file on the IBM i Integrated File System (IFS). For example, Universal Display Files can be used to create:

  • A program that produces dynamic HTML content for a web browser.
  • A web service that outputs an XML or JSON document to a client program via HTTP.
  • A plain text, XML, JSON, or CSV document on the IFS.

The rest of this documentation page will show you how to create an invoice using a Universal Display File. If you aren't familiar with Universal Display Files, you can learn more general information about them here


Below are the files that include the source code for the both the RPG program and the Universal Display File used in this example: 

Universal Display File Design

For this example, we organized our data into 4 separate record formats: header, detail, footer and error. You should separate your logic into multiple record formats based on the data that you want to display. While it’s not necessary to use multiple record formats in your Universal Display File, we recommend this approach to more easily manage your logic. In this example, the data in the header, footer and error record formats is data that we want our program to only output once. Data in the detail record format is written multiple times, depending on the data in the database file that you’re using. In the next few sections of this documentation, we will break down the individual record formats into further detail.


Below is an image of the header format:

Note that the contents of the style tag have been omitted from the screen shot to make it easier to understand. These contents will be included in the full source code, however. You can download the entire source code for this example using the links that are provided earlier in this document.

As mentioned previously, we want the information in the header record format to only be written once when the invoice is created. This record format is a good place to create tables, add the invoice title, include any styling and output any static text you may want for your invoice. In the case of this example, we want certain elements of the invoice to show only once, such as: the ‘Invoice’ title, the company address, the invoice number table, and the item table. Because the header will only be written one time, this information will only be output once when you run the application and create the overall setup of the invoice.

Another important aspect is the ‘input parameters’ property. In this example, we have one input parameter – the invoice number. While we've placed the input parameter on the header record format in this example, it's important to understand that it can be placed on any record format – you will just need to read that particular record format in the RPG program. In our RPG program, we will check if this parameter has been provided and show the correct invoice depending on the invoice number that is given to the program. We've set up this input parameter like so:                

You can see from the image above that we’ve named the parameter ‘invoice’. In this example, 'invoice' will be used as a query string parameter in the URL that will run this program. We’ve also bound the parameter to a nine-digit decimal field called ‘Invoice’. This field will be used in the RPG program, which is discussed later on this page.


Below is an image of the detail record format:

Unlike the header format, data in the detail record format is written as many times as needed until all the data that exists has been outputted to the screen. This record format will display data in the ‘Item’ table for the invoice. In this example, there is only one item for this invoice number, so there is only one item being written to the table. However, if there were other items for this invoice, the logic in the detail record format would continue to write these items to the table until there were no other items to display. 

Footer and Error

Below is an image of the footer record format:

Like the header record format, we want the data in the footer format to only be written once. In this example, the footer record handles the subtotal, tax and total price of the items that are listed in the ‘item’ table. This information will be included as a part of the ‘item’ table (notice that this information is located inside of the <table> tag). Also notice that this format contains the closing main <div>, <body> and <html> tags. Because of this, the footer record format will officially end the HTML page.

The last record format that we have in this example is the error record format:

The error format will display any errors that you may receive when attempting to run this program. For example, the RPG program would display the contents of the error format if the user did not provide the required parameter before attempting to run the program. In this example, if no invoice number is given, the error record format would notify the user that they had forgotten to provide this parameter for the program.

RPG Program

In this section, we will explain our RPG program along with some important details that are needed for this example. Below is the RPG program in its entirety:

// Control Options
       ctl-opt ActGrp(*Caller) Main(Main);
       // Display File
       dcl-f INVOICE01D WorkStn Handler('UNIVERSAL(HANDLER)') UsrOpn;
       // Data Files
       dcl-f INHEADERP Keyed UsrOpn;
       dcl-f INDETAILP Keyed UsrOpn;
       dcl-f CSMASTERP Keyed UsrOpn;
       // Main Procedure
       Dcl-Proc Main;
         Open INVOICE01D;
         Open INHEADERP;
         Open INDETAILP;
         Open CSMASTERP;
         // Get Invoice Number (Query String Parameter)
         Read HEADER; 
         // Find Invoice
         Chain Invoice INHEADERR;
         If Not %Found();
           Message = 'Invoice cannot be found.';
           Write ERROR;
           // Get Customer Name from Customer Master
           CMNAME = '';
           Chain HDCMID CSMASTERR;
           InvDate = %Char(%Date(HDTIME) : *USA);
           Write HEADER;
           // Output Detail Records
           SubTotal = 0;
           SetLL Invoice INDETAILR;
           ReadE Invoice INDETAILR;
           Dow Not %Eof();
             Write DETAIL;
             SubTotal += DTXAMOUNT;
             ReadE Invoice INDETAILR;
           Total = SubTotal + HDSTAXAMT;
           Write FOOTER;
         Close INVOICE01D;
         Close INHEADERP;
         Close INDETAILP;
         Close CSMASTERP;

Notice that in the program above, we are using the Universal Handler when defining the display file. The Universal Display File Handler processes the RPG program’s WRITE and READ operations on a Universal Display File, writing the results to an HTTP response, or an IFS file. You can use the Universal Handler by adding the following to your RPG program:


After defining our display file and declaring the data files that we need for this example, we declare our main procedure. One thing to mention in the main procedure is line 21: Read HEADER. This is where the RPG program reads the header format and gets the input parameter – Invoice – that we defined in our Universal Display File. In this example, we are reading the header format because this is where we defined our input parameter. However, as mentioned previously, you can define the input parameter on any record format as long as you're reading this format in your RPG program. 

Once your program reads the appropriate record format that the input parameter is defined on, you can then use this parameter in your program. In this example, if an invoice number is provided and it is a valid number, then the program should continue as expected and write the remaining record formats: header, detail and footer. However, this program does handle the event that an invoice number isn't passed or an incorrect invoice number is used. You can see this in lines 25 - 27 in the program above. If an invoice number isn't given as a parameter, or if the invoice number that is given cannot be found, then the program will show the error record format along with a message stating that the invoice can't be found. 

Configuring the URL

Before launching the Universal Display File program, you will need to configure the URL using the Web Connector. The Web Connector lets you map a URL like http://server:port/profoundui/universal/myprogram to a program using a Universal Display File. Note that “server” and “port” are simply generic names for example purposes – “server” would be replaced with the actual server name and “port” would need to be replaced with the correct port number for the instance. You can read more about the Web Connector and configuring URL mappings here.

In this case, we’ve set up our URL like so: http://server:port/profoundui/universal/invoice. We want this URL to map to program INVOICE01R in library INVOICE. This is set up in the Web Connector using the following:

URIMAP: /invoice



So, having /invoice in the URL will result in the program INVOICE01R in library INVOICE being called. This is also where we will reference the input parameter that was discussed previously. You will need to provide the invoice number in the parameter of the URL when launching this program in order for this to work as expected. The following is the complete URL used to run this program: http://server:port/profoundui/universal/invoice?invoice=53696.

End Result

Once you have compiled your Universal Display File, compiled your RPG program and configured your URL using the Web Connector, you should be able to launch your program. For example purposes, we are using this program as a stand-alone invoice, however, this program could be used with another program to dynamically set the URL parameter to show different invoices based on the invoice number that is provided. The following image shows the result for invoice number 53696:

  • No labels