Tuesday, October 03, 2017

The 'Check-field' trigger

To quote the documentation, the 'check-field' trigger performs verification checks on a value specified for a form column, and so potentially can be very useful. In forms which are concerned with sales, the status of a given part is checked to see that the 'sellflag' is marked, i.e. the part can be sold to customers. In one specific form, no such check was performed and it was my job to define the required trigger.

There is one very important rule to remember for 'check-field' triggers: do not rely on any form variables! This is because the field has yet to be exited and so other fields have yet to receive any values. An example might help: as soon as a valid part number has been entered, other fields such as price and the internal part number itself get their values. But these fields are empty when 'check-field' is operating.

So how does one get the value of the current field? One uses the special variable :$.@. Thus the trigger which I finally wrote last night looks like this:
ERRMSG 9000 FROM PARTSTATS, PART WHERE PARTSTATS.PARTSTAT = PART.PARTSTAT AND PARTSTATS.SELLFLAG <> 'Y' AND PART.PARTNAME = :$.@;
I use the concise syntax ('errmsg X from ...') which I discussed here.

1 comment:

Yitzchok said...

No'am, if I may:
Most of what you write here, I agree with entirely.
But I think you join different things together in this post for the wrong reasons.

1. You write that since CHECK-FIELD runs before POST-FIELD, some other fields might not have the expected values. It's going to depend on what you expect them to be. Other fields which aren't related to the field being checked should be what the user thinks they should be. Those which are updated by an explicit POST-FIELD naturally won't have been changed yet. The "gotcha" in this case (and maybe what you mean but it's not apparent) is that the implicit POST-FIELDs haven't run yet and therefore if you are perhaps changing PARTNAME where that's eventually going to change PART which is a foreign key on the form table, PART won't have yet been updated to reflect the PARTNAME being checked, so you have to indeed check PARTNAME and not PART.

2. On the other hand (and this is what prompted me to write), that's not the reason to use :$.@. While you don't want to check :$.PART it's not because that doesn't reflect the field value yet, it's because the field value hasn't been updated yet. On the other hand PARTNAME has been changed, just we don't let you go anywhere and leave PARTNAME that way if it's not an acceptable value. You can check :$.PARTNAME (I'm referring to the case where the CHECK-FIELD is on PARTNAME) and you'll get exactly what :$.@ gives you.

The value of the @ is that it is a variable itself, just as $ is.
For the uninitiated, $ is a variable (of sorts, you could perhaps say it's a placeholder) which is expanded to the name of the object (form, procedure etc). So while the CUSTNAME field of the CUSTOMERS form can be referred to (in any form from which CUSTOMERS is reachable) as :CUSTOMERS.CUSTNAME, we tend to be economical and write :$.CUSTOMERS.
In the same way, @ reflects the current field name. If you're in PARTNAME, there won't ever be a difference between :$.PARTNAME and :$.@.
Where is @ particularly useful?
If you need to write a trigger (typically a CHECK-FIELD) which needs to check a value of a certain type (like in this example) which would be useful for multiple fields of the same type. Say you have PART.PARTNAME multiple times such as in PARTNAME and MPARTNAME and you want to do the same check on both of them, you can write a CHECK-FIELD for one and refer to the current field as :$.@ and then #INCLUDE that trigger in the other field and it'll work. Obviously if you were to refer to :$.PARTNAME in that trigger there wouldn't be any point in #INCLUDEing it because on changing MPARTNAME you'd check the value of PARTNAME...

I hope that is informative to you and your readers.