Monday 24 August 2020

Beware of the dollar sign!

At a company for which I am doing piecework programming, I was asked to fix a procedure that was giving an error message about being unable to create a file. I looked at the procedure (one which prints invoices); the problematic code was trying to create a string whose contents - basically the invoice number and customer email - would be displayed as a barcode in the final document.

Why is this problematic? For those who don't know, the character set that can be displayed in a barcode is severely limited to upper case characters, digits and a few characters such as * and $, but not @. Email addresses have the '@' character, so the original programmer had to find a way of replacing this with the '$' character.

In order to achieve this, the original programmer used a baroque set up of creating the initial string (with '@'), writing it to a file, then running the FILTER program three times (!) in order to get the file contents (i.e. the string) into the required format; one run replaced @ with $, one run turned the entire string into upper case; I don't know (nor care) what the third run did. This file was then loaded via an interface into a simple table from which the string was extracted and eventually displayed. Breathtaking in its ingenuity but totally misguided. To be fair, the procedure might have been written for a much earlier version of Priority in which certain functionality might be missing.

The actual problem that the client faced was that the intermediate files were being written to the root of disk C: which is of course a no-no. I simply changed the file directory and the procedure started working. But the sheer complication of this procedure irked me and I was sure that I could find a simpler way of creating the required string.

Here is my first attempt:
SELECT TOUPPER (CUSTOMERSA.EMAIL) INTO :EMAIL FROM CUSTOMERSA, INVOICES WHERE CUSTOMERSA.CUST = INVOICES.CUST AND INVOICES.IV = :$.IV; SELECT STRCAT ('*E', IVNUM, STRPIECE (:EMAIL, '@', 1, 1), '$', STRPIECE (:EMAIL, '@', 2, 1), '*') INTO :$.BC FROM INVOICES WHERE IV = :$.IV;
If the invoice number were IV200001 and the email test@microsoft.com, the resulting string would be expected to be '*EIV200001TEST$MICROSOFT.COM*'. Unfortunately this would not be the result. Using the naked dollar sign ('$') causes the preprocessor in the parser to replace this with the name of the procedure, resulting in something like 
'*EIV200001TESTTEST_WWWSHOWCIV2MICROSOFT.COM*'! This is normally a good thing as it enables one to pass the current procedure name to the ENTMESSAGE function as $, without denoting the name - as a result, the same code can be copy/pasted between procedures without problem.

A method of obtaining the naked dollar sign without using the naked dollar sign is required! After some lateral thinking, I came up with the following, but unfortunately it too inserts the first letter of the procedure's name instead of the dollar sign.
SELECT TOUPPER (CUSTOMERSA.EMAIL) INTO :EMAIL FROM CUSTOMERSA, INVOICES WHERE CUSTOMERSA.CUST = INVOICES.CUST AND INVOICES.IV = :$.IV; :DOLLAR = '1$1'; SELECT STRCAT ('*E', IVNUM, STRPIECE (:EMAIL, '@', 1, 1), STRIND (:DOLLAR, 2, 1), STRPIECE (:EMAIL, '@', 2, 1), '*') INTO :$.BC FROM INVOICES WHERE IV = :$.IV;
Incidentally, I don't see the above as being programming in Priority; it's got nothing to do with working with a database engine. Instead, it's more general programming in the context of the Priority programming language, which isn't something that can be easily taught. One has to remember that in a procedure, '$' is going to be expanded into the procedure's name.

I finally figured out how to solve the problem - use the code of currency -2, which is ... $ (at least, in companies where the default currency is NIS; for companies where the default currency is dollars, the number is -1). The below works in a test procedure on my server, but I will have to implement it fully on the client's server in order to be sure.
SELECT TOUPPER (CUSTOMERSA.EMAIL) INTO :EMAIL FROM CUSTOMERSA, INVOICES WHERE CUSTOMERSA.CUST = INVOICES.CUST AND INVOICES.IV = :$.IV; SELECT STRCAT ('*E', INVOICES.IVNUM, STRPIECE (:EMAIL, '@', 1, 1), CURRENCIES.CODE, STRPIECE (:EMAIL, '@', 2, 1), '*') INTO :$.BC FROM INVOICES, CURRENCIES WHERE INVOICES.IV = :$.IV AND CURRENCIES.CURRENCY = -2;
The above code did not work on the client's server as they have defined the code of currency -2 to be USD. Tired of knocking my head against a brick wall, I swiftly defined a personal table of constants, defined a constant with the value '$' then inserted the appropriate code into the procedure. This finally works!!

No comments:

Post a Comment