2023 | 2022 | 2021 | 2020 | 2019 | 2018 |
---|---|---|---|---|---|
12 | 13 | 14 | 15 | 16 | 17 |
2018 | 2019 | 2020 | 2021 | 2022 | 2023 |
---|---|---|---|---|---|
17 | 16 | 15 | 14 | 13 | 12 |
Pitfalls and tips about advanced programming for the Priority ERP system: what you won't find in the SDK!
2023 | 2022 | 2021 | 2020 | 2019 | 2018 |
---|---|---|---|---|---|
12 | 13 | 14 | 15 | 16 | 17 |
2018 | 2019 | 2020 | 2021 | 2022 | 2023 |
---|---|---|---|---|---|
17 | 16 | 15 | 14 | 13 | 12 |
I wrote the other day about code to calculate the dollar value of sales invoices. I had written code that iterated by means of a cursor over the lines of an invoice and calculated the dollar value of each line, then summing these values. I was sure that there was a better way of doing this but I hadn't managed to find this better way. While I was describing the problem, I saw the quick way of getting the dollar value, using values in the connected transaction. This is yet another case of rubber duck debugging: describing the problem in words leads to a solution. Here is the code that is stored as a trigger in the 'func' form.
/*********************************************************** TEST_CNVINV2DOLLAR - Convert an invoice's total to dollars 11/11/23: New, much simpler version that doesn't require a cursor and so can be used multiple times in a container procedure. SUB 6200 - Input: :IV Output: :QP ************************************************************/ SUB 6200; :VAT62 = :SUM62 = :EXCHANGE62 = :QP = 0.0; :TMP62 = 0E9; :DEBIT62 = '\0'; SELECT INVOICES.VAT, INVOICES.DEBIT, FNCTRANS.SUM2, FNCTRANS.EXCHANGE2 INTO :VAT62, :DEBIT62, :SUM62, :EXCHANGE62 FROM INVOICES, FNCTRANS WHERE INVOICES.FNCTRANS = FNCTRANS.FNCTRANS AND INVOICES.IV = :IV; :TMP62 = :VAT62 / :EXCHANGE62; :QP = (:DEBIT62 = 'C' ? -1 : 1) * (:SUM62 - :TMP62); RETURN;
Short and simple.
Two and a half years ago, I developed a system for version control of procedures. This system has proved invaluable at times when a change to a procedure did not not give the desired output; I could revert the code to the previous version thus restoring the status quo. At one time I extended the system to work with interface code - this was very simple. For some time now, I have been wanting to extend the system to provide version control for screen triggers, but this is not so simple.
In the same way that a procedure can have several SQLI stages, a screen/form can have several triggers. But as opposed to procedures where every SQLI stage has a different value in the PROGRAMS table, triggers are managed in a similar albeit different manner, by means of the FORMTRIG table that has both a pointer to the parent form (e.g. ORDERS) and a pointer to the given trigger.
Originally I thought that I would have to develop a completely new system for form triggers, albeit influenced by the procedures code. But when I devoted some serious thought to the topic, I realised that all I need do was add a new field to the table that manages the versions; this field would be a pointer to the given trigger that along with the pointer to the parent form would completely identify the trigger. Obviously the code that saves a version would have to be written, but this would be very similar to the code that saves a version of a procedure or interface.
Once I had this insight, there wasn't very much that I could do to advance the subject, as the new field would have to be part of the primary key (U) of the manager table that would mean having to add the field to the appropriate form, which is something that I don't do during regular work hours. Fortunately Friday afternoon is a time when no one ever seems to be connected to Priority, so for me it's an ideal time to work on form code.
First I added the new field, then I updated all the existing tuples in the table to have the value -10 in this field; every tuple has to have a value, but it is meaningless when the tuple is for a procedure. Then I had to add this field (and its value) to the existing screen, then prepared all the forms. Then I updated the code for saving procedure versions to include this new field and saved this new version: the form displayed it correctly. Why -10? 0 would appear to be a better value, but I am wary of saving 0 in a key field. There are five predefined triggers (four search triggers and CHOOSE-FIELD) that have values -5 to -1; as I didn't want a collision with these values, I chose a different number. It occurred to me later that a tuple for a procedure couldn't possibly have the same EXEC value as a tuple for a trigger, so I could have used -1 without problem.
Then I had to start work with the trigger code. First I defined a new form for displaying versions that is almost exactly the same as the existing form albeit with different values for the EXEC and TRIG fields. This form is a son of the FTRIG form. Once I had the form defined, I could define the interface for saving a new version, and once I had the interface I could define the required procedure. This is almost a chicken and egg problem, but there is a way through the convoluted path.
Debugging the procedure was a different story to the simple steps that I had taken so far. The first - and relatively minor - problem was that I had neglected to define correctly the TRIG field in the new form. Once this was fixed, I could save new versions of each trigger, but the text - the point of the entire exercise - was not being saved. In true debugging fashion, I took the dog for a walk and considered my options. It turned out that the procedure was saving the code text in GENERALLOAD.TEXT, but the interface was expecting the text in TEXT2. A hard to find but easy to correct bug. It then occurred to me that I could improve the system by saving the date of the trigger as the version date; this saves me some work when saving existing triggers, although of course it won't make any difference when saving a new version of a trigger.
An ounce of prevention is worth a pound of cure.
The comptroller of a company for whom I do contract work complained that two standalone reports that present similar data have different values. These reports give sales invoice totals converted in dollars; the invoices may be in at least three different currencies, some with and some without VAT. Years ago I developed an algorithm for calculating the value of a single line: multiply the quantity by the price of the line, taking into account both the discount on the line and the invoice level discount. Once this is known, this intermediate value can be converted to dollars by means of a routine that I saved as a global trigger (i.e. as a trigger in the 'func' screen), that checks the currency of the line (I've seen invoices that have different currencies in the lines and in the header) and acts accordingly. It looks something like this:
GOTO 110 WHERE :$.CURR = -2; /* dollars */ SUB 900 WHERE :$.CURR <> -1; /* convert to NIS */ SUB 910; /* convert to dollars */ LABEL 110; ... #INCLUDE func/TEST_CNV2DOLLAR
My company is currently in the process of checking the upgrade path to version 23.0; I have come across a problem with some private table interface errors. To be clear, a private interface does not use GENERALLOAD or a filename that is defined in the interface; this type of interface expects a file in the system/load directory whose name is the same as the interface, normally without extension.
In one interface, I defined a table variable DEBIT to be a character value of length 1. The upgrade error report says that this variable is an incompatible data type. I understand this to mean that I have reused a predefined variable name and instead of using the predefined data type - presumably int - I am redefining it as a character. This results in a page full of errors in the upgrade error report. The fix, of course, is simple - change the variable name to DEBITXX, or similar; of course, the code that inserts the data into the load table must also be changed.
In another interface, the same type of error occurs with a variable named DUEDATE (but not with a variable named QUANT). Again, the fix is simple.
Your experience, of course, may differ.