Sunday, 29 December 2024

How to crash the syntax checker

I wrote a procedure the other day in a rather hurried manner that was intended to round prices in a price list. The syntax checker crashed repeatedly, creating a dump file, when I checked this procedure, both in the Web interface and the Windows interface. I found what the offending statement was by the tedious process of removing as many lines as possible, then adding back individual parts until the program crashed again.

It was a 'simple' error - :QUANT = REALQUANT (1.0). The function REALQUANT expects to receive an integer for its input and outputs a real. In the above call, I am passing a real to the function that causes it to explode. Had I not been writing so fast, I would have noticed the error before checking the syntax as the expression was inside an INSERT INTO clause, and the expression was being saved in a integer field.

The opposite mistake, INTQUANT (1), gives the error 'Parameter for function must be of REAL type'. It's a shame that there's no error message for REALQUANT (1.0).


On a slightly different topic, I wanted to see whether the AI program CoPilot can help in writing procedures. I gave it a complete procedure - the one described above - and all CoPilot did was add obvious comments that are totally superfluous. I then asked whether CoPilot could improve the procedure - it introduced new control statements such as WHILE and ENDWHILE that would be great if they were allowed, but sadly no.

Asking CoPilot to write a procedure to give data about items in invoice lines was a waste of time - half of the garbage that CoPilot returned came straight from my original procedure (including linking GENERALLOAD and checking for errors after EXECUTE INTERFACE) and the original half was also garbage.

It seems at the moment that I am better at writing Priority procedures than this albeit free AI program.

In CoPilot's defence, I will note that originally it presented some code written for the Rest API: this code may be correct but I didn't look at it.

Tuesday, 17 December 2024

Adding line items to an existing document

There is a section in the SDK with the above title (in chapter 6, 'Documents) that reads as follows: When an interface that adds line items to a document is executed, by default the new items are inserted first in the document (that is, they receive a smaller line number than existing records). For instance, if a given interface adds lines to an existing order that contains two lines, the new record will appear on line 1, the first existing order item will move to line 2 and the second existing order item will move to line 3. Unfortunately this is true. Following this is an explanation of how to insure that new lines will be added at the end of the document, but as usual, this isn't particularly clear (or at least, not clear to me).

As it happens, I have a procedure that I run most days that may add lines to a warehouse transfer document (form DOCUMENTS_T). I won't go into the whys and wheres of this, but I do know that whenever I run this, lines will be added to the beginning of the warehouse transfer and not at the end. For my purposes, it doesn't matter where the lines are added, but I want to use this example in order to deepen my understanding as I have a procedure for an external client that does something similar, except that there I am adding a line to an invoice, and for reasons that aren't relevant, it's very important that the added line be at the end.

So what does one do? In the case of the warehouse transfer document, I added to the interface for form TRANSORDER_T the field TRANS (saved in INT10), where TRANS is the automatic key of the table and is also hidden. At first I thought that the following would  suffice

SELECT MAX (LINE) INTO :LINE FROM TRANSORDER WHERE DOC = :DOC; SELECT TRANS INTO :TRANS FROM TRANSORDER WHERE DOC = :DOC AND LINE = :LINE; INSERT INTO GENERALLOAD (LINE, RECORDTYPE, INT10) VALUES (2, '2', :TRANS);

Although TRANS is the A key, the form is sorted by LINE, so I thought it best to get the TRANS of the last line then insert this into GENERALLOAD. Lines that the procedure will add will come after this line.

The interface manager complained that line 2 didn't have a part. This led me to realise that I need to enter all of the necessary fields that already exist in TRANSORDER, as well as TRANS into GENERALLOAD before the added lines. Thus my procedure now contains the lines

INSERT INTO GENERALLOAD (LINE, RECORDTYPE, INT1) VALUES (1, '1', :DOC); INSERT INTO GENERALLOAD (LINE, RECORDTYPE, INT10, TEXT1, INT1, TEXT13, TEXT17, TEXT18) SELECT 1 + SQL.LINE, '2', TRANSORDER.TRANS, PART.PARTNAME, TRANSORDER.QUANT, CUSTOMERS.CUSTNAME, CUSTOMERS.CUSTNAME, WAREHOUSES.LOCNAME FROM TRANSORDER, PART, CUSTOMERS, WAREHOUSES WHERE TRANSORDER.PART = PART.PART AND TRANSORDER.CUST = CUSTOMERS.CUST AND TRANSORDER.WARHS = WAREHOUSES.WARHS AND TRANSORDER.DOC = :DOC; SELECT MAX (LINE) INTO :LINE FROM GENERALLOAD;

I tested this by deleting the last few lines of the existing warehouse transfer, then running the procedure. If the above code works correctly, then the lines that I deleted should be restored to the document and they should be the final lines. Indeed they were added at the end.

What about invoiceitems, especially considering that this table doesn't have an automatic key but rather a composite key of IV and KLINE? The following works nicely to copy the lines

INSERT INTO GENERALLOAD (LINE, RECORDTYPE, INT1) VALUES (1, '1', :IV); INSERT INTO GENERALLOAD (LINE, RECORDTYPE, INT10, TEXT1, INT1, REAL1) SELECT 1 + SQL.LINE, '2', INVOICEITEMS.KLINE, PART.PARTNAME, INVOICEITEMS.TQUANT, INVOICEITEMS.PRICE FROM INVOICEITEMS, PART WHERE INVOICEITEMS.PART = PART.PART AND INVOICEITEMS.IV = :IV; SELECT MAX (LINE) INTO :LINE FROM GENERALLOAD; .... /* then new lines get added without INT10 */ :LINE = :LINE + 1; INSERT INTO GENERALLOAD (LINE, RECORDTYPE, TEXT1, INT1, REAL1) VALUES (:LINE, '2', '11055332YA307S', 6000, 250.0);

This new line does get added at the end of the invoice.