Tuesday, January 12, 2016

Sending complex emails via Priority

The genesis of this blog was a request made to me by a programmer of intermediate skill who wanted to send a complex email from a screen trigger in Priority.

There is a simple mechanism (half documented) called MAILMSG - this sends a mail to either a user or to a group, where the contents are contained in three parameters, each of maximum length 64 characters. This mechanism is usable for small amounts of data, such as 'order XC123 has been opened', but is somewhat limited, both in content and in format.

A much more complicated mechanism exists (again, half documented) which allows one to send a report via email. Once this technique is mastered, it's actually very useful, but quite hard to debug.

Neither of these options were what the programmer required, Basically he wanted a stronger version of MAILMSG which could include almost unlimited text. A third programmer suggested using SENDMAIL, but gave no hints as to what this might be or how it might be accomplished.

When I started poking around, I discovered that SENDMAIL is an external program which sends mail (naturally); its payload appears to be a row stored in the MAILBOX table. After turning this over in my mind, I realised how this mechanism works. There is a form in Priority called MAILBOXSEND which is a compromise between a normal Priority form and a 'send email' form of a program like Outlook: there is a header (subject, recipient) and a son form which consists of text. When sending mail, the contents of this screen are passed to the external program which sends the mail.

Once this was clear, writing the program itself was relatively painless. Data can be entered into a new row of the MAILBOX table by the standard means of an interface; I wrote the necessary code and checked that indeed a new email was stored and waiting to be sent.

More problematic was the actual sending of the email. In the MAILBOXSEND screen, this is implemented by a procedure with a few steps, of which one - the actual SENDMAIL procedure - interested me. This step has two parameters: a linked file of emails and a text parameter. By oversight, I neglected to define the order in which the parameters were passed to the procedure, and of course, the parameters were being passed in the wrong order which is why my program didn't work at first. After a while I realised my mistake, corrected it and discovered that my program does indeed send mail.

The program, written to be implemented as a trigger in the PORDERS form, follows. The variables whose name start with :$. refer to screen fields.
/* Build the email */ SELECT SQL.TMPFILE INTO :GEN FROM DUMMY; LINK GENERALLOAD TO :GEN; INSERT INTO GENERALLOAD (LINE, RECORDTYPE, TEXT, TEXT6) VALUES (1, '1', STRCAT ('*', :$.ORDNAME), 'TAMCHIR'); /* tamchir is the name of an email group */ INSERT INTO GENERALLOAD (LINE, RECORDTYPE, TEXT) VALUES (2, '2', STRCAT ('Supplier = ', :$.SUPDES)); INSERT INTO GENERALLOAD (LINE, RECORDTYPE, TEXT) SELECT PORDERITEMS.LINE + 2, '2', STRCAT (PART.PARTNAME, ', ', PART.PARTDES, ', quantity = ', RTOA (REALQUANT (PORDERITEMS.QUANT), 2)) FROM PORDERITEMS, PART WHERE PORDERITEMS.PART = PART.PART AND PORDERITEMS.ORD = :ORD ; EXECUTE INTERFACE 'GLOB_SENDMAIL', SQL.TMPFILE, '-L', :GEN; /* Did the interface execute with errors? */ :XMSG = ''; SELECT MESSAGE INTO :XMSG FROM ERRMSGS WHERE USER = SQL.USER AND TYPE = 'i'; GOTO 1 WHERE :RETVAL <= 0; :PAR1 = STRIND (:XMSG, 1, 64); :PAR2 = STRIND (:XMSG, 65, 64); :PAR3 = STRIND (:XMSG, 129, 64); ERRMSG 500; LABEL 1; /* Get the id of the email, now that it has been built */ :MB = 0; SELECT ATOI (GENERALLOAD.KEY1) INTO :MB FROM GENERALLOAD WHERE LOADED = 'Y' AND RECORDTYPE = '1'; /* Insert that email into a temporary file of emails */ SELECT SQL.TMPFILE INTO :PAR FROM DUMMY; LINK MAILBOX TO :PAR; INSERT INTO MAILBOX SELECT * FROM MAILBOX ORIG WHERE MAILBOX = :MB; EXECUTE SENDMAIL :PAR, :XMSG; /* send it */ UNLINK AND REMOVE MAILBOX; UNLINK AND REMOVE GENERALLOAD;
This program is an example of what might be called 'incremental improvement'. It's not something which is taught on the first day of Priority programming, but rather builds on knowledge which is incremented as one learns more and more techniques. Almost the entire program - until the comment 'Insert that email' - is fairly standard code for transferring data from one screen/table to another screen/table; whilst the specifics vary from application to application, the general structure and tasks are the same. I should point out that this technique is considered 'advanced', although once learned, it is extremely useful. 

Only the six lines of code following that comment are new (and to be honest, the first five, which copy one row from the existing MAILBOX table in a new, temporary, table are based on similar code by which a customer order is built from data in a storage area). Calling the SENDMAIL program within my procedure prevents the problem which I had earlier about parameter ordering: here it is clear what the order is. 

There is no way of finding any code within Priority which calls SENDMAIL in this manner (one can't search within the SQL code of a procedure), so one can't learn this technique from previous examples without some head-scratching.

[SO: 4057; 3, 18, 38
MPP: 632; 1, 3. 6]

No comments: