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.

No comments:

Post a Comment