Friday, 29 May 2020

Calling a procedure from a screen trigger

I am starting work as a sub-contractor (i.e. someone else is the consultant who interfaces with the client and defines their work procedures whereas I'm the programmer who implements those procedures in Priority) for a company that wants to have one master company in which are defined the customers, suppliers, parts, etc. and satellite companies in which the actual transactions take place. My primary task is to write interfaces that will automatically transfer data from the master company to the satellites. 

For some tables (e.g. supplier type), new/updated data will be copied to all the satellites, whereas for other tables (e.g. customers), the user can decide to which satellites will be copied the new/updated customer. Upon hearing this, I realised that I have to define a table with four fields: the id of the new datum, the type of the new datum, satellite name and whether the data should be copied to this satellite. I also developed a form based on this table; this forms will be a 'son' form to all the forms that allow a choice of satellite. This form is easy to define as it takes its inspiration from another general form, EXTFILES. The fun starts in the copying procedure.

Within Priority, there exists a documented method for sending data from one procedure or trigger to another procedure or report. I use this frequently when building procedures which by means of the scheduler will send reports by mail. The procedure uses a local copy of one of the 'stack' tables (normally STACK which has precisely one field - ELEMENT - which is of course the table's primary key). For example,
:GROUP = 'NO_CNC'; EXECUTE WINACTIV '-R', 'TEST_NOPROD_METAL', 'STACK', :$.STK, '-g', :GROUP;
My original thought would be to do something similar in the form's post-form trigger: pass the parent form's id number to a procedure that then iterates through the chosen satellites, invoking an interface to do the actual addition to the satellite. As each procedure/interface is specific to a given form (i.e. the code necessary for updating customers is similar but different to the code necessary for updating suppliers), there is no need to pass the data's type (customer, etc) to the procedure. As only one datum is required, I could use the STACK table as shown above.

The communal form's POST-FIELD trigger had code like this:
GOTO 2 WHERE :TEST_CHANGED = 0; SELECT SQL.TMPFILE INTO :FILE FROM DUMMY; LINK STACK TO :FILE; INSERT INTO STACK (ELEMENT) VALUES (:$$.NSCUST); GOTO 2 WHERE :RETVAL <= 0; GOTO 1 WHERE :$$.EXTTYPE <> 'p'; /* not a part */ EXECUTE BACKGROUND ACTIVATE '-P', 'TEST_COPYPART', 'PART', :FILE; GOTO 2; LABEL 1; GOTO 1 WHERE :$$.EXTTYPE <> 'C'; /* not a customer */ EXECUTE BACKGROUND ACTIVATE '-P', 'TEST_COPYCUST', 'PART', :FILE; GOTO 2; LABEL 1; ... LABEL 2; UNLINK AND REMOVE STACK;
This seemed reasonable but it didn't work! I spent a very frustrating hour discovering that the procedure was not receiving data. It transpires that every procedure has to have its data passed in a linked table of the correct type: the 'copy part' procedure has to receive a linked table of parts and the 'copy customer' procedure has to receive a linked table of customers, etc. So the final code became
GOTO 2 WHERE :TEST_CHANGED = 0; SELECT SQL.TMPFILE INTO :FILE FROM DUMMY; GOTO 1 WHERE :$$.EXTTYPE <> 'p'; /* not a part */ LINK PART TO :FILE; INSERT INTO PART SELECT * FROM PART ORIG WHERE PART = :$$.NSCUST; EXECUTE BACKGROUND ACTIVATE '-P', 'TEST_COPYPART', 'PART', :FILE; UNLINK AND REMOVE PART; LABEL 1; GOTO 1 WHERE :$$.EXTTYPE <> 'C'; /* not a customer */ LINK CUSTOMERS TO :FILE; INSERT INTO CUSTOMERS SELECT * FROM CUSTOMERS ORIG WHERE CUSTOMER = :$$.NSCUST; EXECUTE BACKGROUND ACTIVATE '-P', 'TEST_COPYCUSTOMER', 'CUSTOMERS', :FILE; UNLINK AND REMOVE CUSTOMERS; LABEL 1; ... LABEL 2;
Using the BACKGROUND parameter after EXECUTE means that the actual copying occurs in the background and so allows the user to continue working without interruption.

One final problem to look out for: users must be able to execute the copying procedures, which means that they have to appear in a menu for which regular users have access. 

2 comments:

  1. From what you write ("the 'copy part' procedure has to receive a linked table of parts") it sounds as if a procedure which copies parts *has* to take PART as the input. While that generally is what happens, it's not a "must".

    As you explain (or at least imply) here, everything has to line up - the table you link your filename to, the table you tell the procedure it's getting, and the table it's expecting to get (in the definition of PAR in the INPUT step). But there's no requirement that that table has to be anything in particular, solely based on what's being done with it.

    I get the impression that these are your own procedures so you could have defined them however you wanted, and I don't see any reason why your first version couldn't have worked... so long as in both procedures (TEST_COPYPART and TEST_COPYCUST) PAR was defined as being a linked table to STACK.

    On the other hand if these are existing or standard procedures which you don't want to, or can't, change, then you need to pass whatever table the procedure's expecting...

    ReplyDelete
  2. I also thought that using STACK to pass the part number would work, but believe me, it didn't. I spent enough time banging my head against the computer in frustration before abandoning the STACK code.

    ReplyDelete