Tuesday 10 September 2024

An advanced subversion of HTML documents

I've written several times about HTML documents in the past, primarily about what I call 'container documents'. This time around, I am writing about an HTML document in its regular sense: the creation of an invoice document, but of course subverting it to display something for which the HTML document framework was not designed.

The customer sells electricity; they want an invoice to show the lines for each electricity meter contained in the invoice (so far, standard functionality) ... and then they want a separate, additional, page for each meter - very much non-standard. In order to create a page for each meter, the HTMLCURSOR of the procedure should return a meter (i.e. SERNUMBERS.SERN) from those contained in the invoice. But the procedure has to be passed an invoice number (or maybe a range of invoices), so how can one display what would appear to be a standard invoice along with non-standard extras?

'Obviously' (it was far from obvious when I started work on this) the list of invoices passed to the procedure in the PAR parameter has to be somehow saved as something else (I used STACK2), then the PAR parameter has to be repositioned in order to receive a list of meters in each invoice. My original code for this pre-HTMLCURSOR stage was

/* Stage 20: Save the invoices passed as a parameter to this procedure */ :PAR1 = '$'; LINK STACK2 TO :$.ST2; ERRMSG 1 WHERE :RETVAL <= 0; LINK INVOICES TO :$.PAR; ERRMSG 1 WHERE :RETVAL <= 0; INSERT INTO STACK2 (ELEMENT, TYPE) SELECT SQL.LINE, IV FROM INVOICES WHERE IV > 0; UNLINK AND REMOVE INVOICES; :$.IDX = 0; /* global iterator */ SELECT MAX (ELEMENT) INTO :$.MAX FROM STACK2; UNLINK STACK2; SELECT SQL.TMPFILE INTO :$.PAR FROM DUMMY;

The IDX and MAX variables are necessary for looping through the list of invoices, as will be seen in the next stage that begins as follows

/* Stage 30: Set up for HTML document */ :$.HDR = 1; LINK STACK2 TO :$.ST2; :$.IDX = :$.IDX + 1; SELECT TYPE INTO :$.IV FROM STACK2 WHERE ELEMENT = :$.IDX;

Following this originally was this code

UNLINK AND REMOVE SERNUMBERS; LINK SERNUMBERS TO :$.PAR; INSERT INTO SERNUMBERS SELECT * FROM SERNUMBERS ORIG WHERE EXISTS (SELECT 1 FROM INVOICEITEMS WHERE SERNUMBERS.SERNUM = INVOICEITEMS.TEST_SERN AND SERNUMBERS.SERN > 0 AND INVOICEITEMS.IV = :$.IV);

Then in the next stage, HTMLCURSOR would retrieve a value from :$.PAR, i.e. SERNUMBERS, and the appropriate documents would be created. Except this didn't work!

After some trial and error, I discovered that if the PAR parameter initially contains (in this case) invoices, as defined in the 'table name' of the line for the parameter, then it has to contain invoices all the time. Redefining PAR and linking it to SERNUMBERS doesn't work. So how can I pass a list of devices? By saving SERN and SERNUM (i.e. the A and U keys) of SERNUMBERS in a linked INVOICES table! It doesn't matter that an invoice with this key number may not exist; eventually I will not be accessing an invoice but a device. For the moment, the INVOICES table is simply a database table that fits my requirements, regardless of what its purpose is within Priority. Here is the continuation of stage 30 - still before HTMLCURSOR.

/* It seems that PAR always has to be linked to invoices. So insert into the linked table values from SERNUMBERS */ UNLINK AND REMOVE INVOICES; LINK INVOICES TO :$.PAR; ERRMSG 1 WHERE :RETVAL <= 0; INSERT INTO INVOICES (IV, IVNUM) /* SUBVERSION!!! */ SELECT SERN, SERNUM FROM SERNUMBERS WHERE EXISTS (SELECT 1 FROM INVOICEITEMS WHERE SERNUMBERS.SERNUM = INVOICEITEMS.TEST_SERN AND SERNUMBERS.SERN > 0 AND INVOICEITEMS.IV = :$.IV);

The HTMLCURSOR stage is simply 'SELECT IV FROM INVOICES WHERE IV > 0'. The SQLI stage following this begins as follows

/* Stage 50: This turns an 'invoice' number back into a sernumber */ SELECT SERN INTO :$.SRN FROM SERNUMBERS WHERE SERN = ATOI (:HTMLVALUE);

Stage 50 contains all the complicated boiler-plate code from the regular procedures, e.g. stage 10 of WWWSHOWCIV. In order to make the entire document display, I had to change certain statements of that boiler-plate code, specifically replacing any usage of ATOI (:HTMLVALUE) with :$.IV - this is what the code expects to receive. In the body of the procedure I am including standard reports such as WWWLOGO and WWWDOCNUM along with special reports of mine. The standard reports receive the :$.IV parameter and the special reports receive :$.SRN. Finally at the end, prior to the closing INPUT stage, there is a small SQLI stage with the following statements

:$.GO = (:$.IDX < :$.MAX ? 30 : 500); :$.HDR = :$.HDR + 1;

Should there have been more than one invoice in the original list passed to the procedure, then the procedure will loop back to stage 30, where the subverted list of invoices is unlinked and then a new list created. The :$.HDR variable is needed so that the initial summation page can be printed for each invoice in the original invoice list, but not printed for every meter returned by HTMLCURSOR. This is something internal to this procedure and cannot necessarily be generalised.

This actually works! I very much doubt that the customer realises how much subversion is required to supply what is asked for.

Wednesday 4 September 2024

An insight into displaying HTML text in a report

I hope that I am not revealing something new that one has to use the DAYS table in order to display HTML text in a report - e.g. WWWORD_2XT. This is even documented on page 105 of the SDK for version 21.0 in a section entitled 'Displaying HTML Text in Reports'.

I have always thought that the use of the DAYS table was some kind of magical sign to the program sitting behind the report generator that HTML text has to be shown (in the same way that any field containing the strings FILENAME or BIRTHDAY will act in a special way).

It turns out that this is not true. Idly I was looking at some standard reports when I came across the procedure COMPMSG; the SQLI stage of this procedure links the STACK table and enters two tuples, 1 and 2. I wondered what this would do in the report: line 30 has this expression

(STACK.ELEMENT = 1 ? EXTMSG.MESSAGE : EXTMSGTEXT.TEXT)

In other words, one doesn't have to use the DAYS table to display HTML text in a report; it's simply convenient as this table already has tuples 0 and 1 (it has more, but that's not relevant). And so my 'magic value' hypothesis is proven false. Intriguingly there is no use of EXTMSGTEXT.TEXTLINE in this report.

Will I use this option? I doubt it, as one has to set up the linked STACK table first whereas the DAYS table is always available.