Monday, 16 February 2026

Error messages from a procedure invoked by a form trigger are not displayed

I have a private POST-UPDATE trigger in form ORDERS that invokes a procedure whose task is to extract certain lines from the current order and insert them into a new order (this isn't particularly relevant). The procedure checks certain data and displays error messages if the checks fail. It transpires that these error messages are displayed when the procedure is run from a menu, but not when invoked from a form, which is when they are needed the most.

Below I describe a slightly complicated way to get around this problem. First I defined a new table TEST_ERRMSGS that has four fields: AKEY, USER, PROG and MESSAGE, where the first three fields create the composite primary key ('U' in Priority-speak). I can't be sure that someone else is going to run the same procedure, probably on a different order but who knows, so I need maximum 'separation'. AKEY will be the order number, USER is of course the user's number and PROG is the number of the procedure. Here is the relevant part of the form trigger pre-enhancement

SELECT SQL.TMPFILE INTO :ECORDERS FROM DUMMY; LINK ORDERS OEC TO :ECORDERS; GOTO 999 WHERE :RETVAL <= 0; INSERT INTO ORDERS OEC SELECT * FROM ORDERS ORIG WHERE ORD = :$.ORD; EXECUTE WINACTIV '-P', 'TEST_SEPARATE', 'ORDERS', :ECORDERS; UNLINK AND REMOVE ORDERS OEC;

Here are the lines that have to be added before the line 'EXECUTE WINACTIV'; one private integer field is utilised although this could be any field in the order as long as the procedure doesn't need it in the course of its normal execution. There's no need to worry about overwriting important data as it's a linked table that is being updated. Of course, it's best to use a private field. 

:TEST_PROG = 0; SELECT EXEC INTO :TEST_PROG FROM EXEC WHERE TYPE = ‘P’ AND ENAME = 'TEST_SEPARATE' ; SELECT SQL.TMPFILE INTO :ECORDERS FROM DUMMY; LINK ORDERS OEC TO :ECORDERS; GOTO 999 WHERE :RETVAL <= 0; INSERT INTO ORDERS OEC SELECT * FROM ORDERS ORIG WHERE ORD = :$.ORD ; UPDATE ORDERS OEC SET TEST_FIELD = -1 /* this signifies that the procedure is called from an order */ WHERE ORD = :$.ORD;

At the beginning of the procedure, the following query is executed to get the order number

LINK ORDERS TO :$.PAR; ERRMSG 1 WHERE :RETVAL <= 0; SELECT ORD, ORDNAME, CUST INTO :OLDORD, :ONAME, ::CUST FROM ORDERS WHERE ORD > 0; UNLINK ORDERS;

To the SELECT list, I add the private field TEST_FIELD that is selected into the variable :FROMFORM. After this is the block

GOTO 10 WHERE :FROMFORM <> -1; :TEST_PROG = 0; SELECT EXEC INTO :TEST_PROG FROM EXEC WHERE TYPE = 'P' AND ENAME = '$'; DELETE FROM TEST_ERRMSGS WHERE USER = SQL.USER AND AKEY = :ORD AND PROG = :TEST_PROG; LABEL 10;

In other words, if there is already an entry in the table for this user, order and procedure, clear it so that we can start afresh. Now the procedure runs, and every time that it detects an error, the rather cumbersome code that appears below has to be included. If before the code was

ERRMSG 62 FROM ORDERSA WHERE ORD = :ORD AND TEST_CONDITION = 'Y';

It now becomes

:ERR = 0; SELECT 62 INTO :ERR FROM ORDERSA WHERE ORD = :OLDORD AND TEST_CONDITION = 'Y'; GOTO 862 WHERE :ERR = 0; GOSUB 9990; ERRMSG 62; LABEL 862; ... SUB 9990; GOTO 9991 WHERE :FROMFORM <> -1; SELECT ENTMESSAGE ('$', 'P', :ERR) INTO :PAR1 FROM DUMMY; INSERT INTO TEST_ERRMSGS (AKEY, USER, PROG, MESSAGE) VALUES (:OLDORD, SQL.USER, :GLOB_PROG, :PAR1); LABEL 9991; RETURN;

Going back to the form trigger, after the line 'EXECUTE WINACTIV', the following has to be added

SELECT MESSAGE INTO :PAR1 FROM TEST_ERRMSGS WHERE USER = SQL.USER AND AKEY = :$.ORD AND PROG = :TEST_PROG; ERRMSG 990 WHERE :RETVAL = 1;

In other words, without digging into the code, the calling trigger has to signify to the procedure that it is being run from a trigger (TEST_FIELD = -1). The procedure then inserts a record into a special table if an error is detected, where the record includes the order number, the user, the procedure and the error message to be displayed. When the trigger resumes execution, it checks whether there is a suitable record and if so, displays the error message. This means that the record will remain in the table but this doesn't matter much for if the user runs the same procedure on the same order, the record will be deleted at the beginning of the procedure.

Wednesday, 21 January 2026

The single character bug returns

This morning I was tasked with retrieving the location of inventory within a specific warehouse. I wrote the following code that is part of a larger procedure that creates a warehouse transfer document.

:LOC = '0'; /* default value */ SELECT WAREHOUSES.LOCNAME INTO :LOC FROM WAREHOUSES, WARHSBAL WHERE WAREHOUSES.WARHS = WARHSBAL.WARHS AND WARHSBAL.PART = :PART AND WAREHOUSES.WARHSNAME = 'Main';

When the procedure containing this code was run, there was a complaint that location R does not exist for warehouse Main. This error was doubly confusing - at first, I thought that the R might refer to the part's type, which would make no sense whatsoever. When I ran the snippet in Windbi, the result was something like R.1.2, which was when I understood what was happening.

:LOC is initially set to the single character '0', for if the retrieval fails (for example, if the part has no inventory) then at least :LOC will have a value that is guaranteed to exist. Unfortunately, as I discovered several years ago1, initialising a string with a single character confuses Priority who thinks that this is a character variable, not a string, and so saving further values into this variable will only save the first character.

The solution is to have an initialisation line at the beginning, like :LOC = '00000000' (I imagine that the length doesn't matter too much, as long as it's longer than one character. Once this line had been added, the addition to the procedure worked flawlessly. 

I notice that I wrote in the earlier blog that this issue had been fixed in version 21. I'm not sure with which version I was working today (it was for an external client), but it was web-based and probably not less than version 23.

Internal links
[1] 51

Tuesday, 20 January 2026

WARNINGYES

It's very easy to define a business rule: if such and such happens, then either: display an error message, display a warning message, send an email, send an SMS, open a task or send a push message (whatever that is). A warning message displays some text along with two buttons, "continue" or "cancel". I've never noticed until now that the default button is "continue". Thus it's very easy to consider the scenario whereby a user does something that causes a rule to fire, but the user ignores the warning message and simply presses "continue", thus basically negating the whole point of the warning.

I discovered (very belatedly) that there is a system constant, WARNINGYES, that controls which button is the default; 1, which is the predefined value, means that the "continue" button is the default, whereas 0 means that "cancel" is the default. I changed the value of the constant to 0 and now hopefully users will actually read any message displayed.

This is to a certain extent shooting myself in the foot: once every couple of days I have to create delivery notes from packing lists (don't ask), and when I paste an order number into the delivery notes screen, two or three messages are displayed. I don't read them and simply press enter to continue. Now I'm going to have to press an arrow key and then enter. The message are generally "Packing list X is connected to this order" and some text referring to the customer. Because I belong to the 'tabula' users group, I can't execute the privileges program, otherwise I would turn these warning messages off. Such are the tribulations of a system manager.

I made that change 20 minutes ago and no one has written to me about it yet. Normally a change like that gets noticed very quickly.