In codeunit 1, there are 4 functions that I hope someone can tell me how they are called. These are: OnGlobalInsert OnGlobalModify OnGlobalDelete OnGlobalRename I think that they are used in connection with GetGlobalTableTriggerMask but I am not sure ??? alternatively, what I need to know is this… Is there any function that is ALWAYS called when ANY form is closed as I dont want to code on each and every form TIA [:D] Dean
The triggers are always automatically called when a record is modified/deleted/inserted/renamed by a form. Unfortunately the triggers do not get called when you call a rec.INSERT(TRUE) in the code [V] The Globaltriggermask stuff is responsible for the decision if a certain change needs to be logged in the change log.
Can we ask you what function/process you’d like to run when any form is closing?
quote:
The triggers are always automatically called when a record is modified/deleted/inserted/renamed by a form
Hi Thomas, this is what I had hoped for, but when I placed a message to test this in any of the triggers I did not get this message ??? Maybe there is something that I am missing ??
quote:
Can we ask you what function/process you’d like to run when any form is closing?
Hi David, Sure you can… Basically I want to extend the error checking capabilites of the “NOTBLANK” properties of any given field as this only check when the field has been selected. I also need to make this so that the user can choose what fields MUST have a value. So basically I am trying to replicate the “Change Log” setup where you can select the table and fields to be checked. Any Suggestions greatfully accepted as I am struggling with working with RecordRef and FieldRef until such time as I get chance to investigate it in more detail. Thanks again. Dean.
For the NotBlank property, the function TESTFIELD will probably do what you want. If you want to make a field mandatory, you might prefer to put it at the table level (OnModify trigger, and sometimes OnInsert). I’m not sure about the value of building a generic solution to do that, since those changes are usually affecting few tables in the database for a customer. If for example you only need 10 fields mandatory in 3-4 different tables, it would be faster to do the changes directly in the table triggers… You can also add a setup table to state if a value is mandatory or not. Ex: Sales Line OnModify()MySetupTable.GET IF MySetupTable."Variant Code Mandatory" and Type = Type::Item THEN TESTFIELD("Variant Code");
If you build this generic solution, please let us know… we might be interested in that kind of solution.
quote:
The Globaltriggermask stuff is responsible for the decision if a certain change needs to be logged in the change log.
Originally posted by tb@softsys.at - 2005 Feb 10 : 08:38:20
To take it a bit further, GetGlobalTableTriggerMask returns an integer valued 0-15 that indicates which GlobalTableTrigger should be executed. Since the integers between 0 and 15 can be represented by 4 bits, each bit marks a particular trigger to be run (insert, modify, delete, rename). Change log uses this to signal which triggers to fire for a given table, but it could be used by other processes as well.
Yeap, that is perfectly right, just one fact needs to be mentioned here as well. As long as GlobalTriggermask returns 0 the global triggers are not getting called. You could of course use this fact to change the code in the GetGlobalTriggerMask function in CU 1 and always return 15: EXIT(15); instead of EXIT(ChangeLogMgt.GetTableTriggerMask(TableID)); You just need to be aware of the fact that you would slow down the system as now the triggers are really always getting called.
Just to prevent a posting to tell me that I’m a “Q&D” programmer [;)]: Another option would be to change the GetGlobalTriggerMask in the Change Log Management Codeunit to not only check the tables that are assigned to the change log management but also the tables assigned to your “NOTBLANK” checking routines. (I mean that would be the “Clean” version whereby the EXIT(15) would by the “Quick & Dirty” version.
quote:
If for example you only need 10 fields mandatory in 3-4 different tables, it would be faster to do the changes directly in the table triggers… You can also add a setup table to state if a value is mandatory or not. Ex: Sales Line OnModify()
Had thought of that one, but that means coding on each and every table which is exactley what I want to avoid. Your spot on with your theory though as I HAVE to make a more generic solution. Will keep you updated.
quote:
To take it a bit further, GetGlobalTableTriggerMask returns an integer valued 0-15 that indicates which GlobalTableTrigger should be executed. Since the integers between 0 and 15 can be represented by 4 bits, each bit marks a particular trigger to be run (insert, modify, delete, rename).
Jack, you wouldn’t happen to Know the values that call the global triggers would you ? [?] Also, is this written into the exe file or something as I could not locate any code that once the value is returned, it then tells Navision what trigger to run [:(]
Exactly, the FIN.exe itself determines based on the result of the function with the ID 20 if one of the following trigger has to be called. Only if the function returns 1,5 or 15 and you insert a new record in a table the function OnGlobalInsert is getting called. So you can control if a global trigger is getting called by changing the behavior of the GetTableTriggerMask function in CU423. Example: Change in CU 423: GetTableTriggerMask(TableNumber : Integer) : Integer EXIT(15); // Exit immediately for calling ALL triggers
In CU 1: OnGlobalInsert(RecRef : RecordRef) ChangLogMgt.LogInsertion(RecRef); MESSAGE('Insert %1',RecRef.GETPOSITION(TRUE)); OnGlobalModify(RecRef : RecordRef;xRecRef : RecordRef) ChangLogMgt.LogModification(RecRef,xRecRef); MESSAGE('MODIFY %1',RecRef.GETPOSITION(TRUE)); OnGlobalDelete(RecRef : RecordRef) ChangLogMgt.LogDeletion(RecRef); MESSAGE('DELETE %1',RecRef.GETPOSITION(TRUE)); OnGlobalRename(RecRef : RecordRef;xRecRef : RecordRef) ChangLogMgt.LogRename(RecRef,xRecRef); MESSAGE('Rename %1',RecRef.GETPOSITION(TRUE));
Then the messages are getting shown
quote:
you wouldn’t happen to Know the values that call the global triggers would you ? [?] Also, is this written into the exe file or something as I could not locate any code that once the value is returned, it then tells Navision what trigger to run [:(]
Originally posted by deanaxon - 2005 Feb 11 : 03:25:47
Each trigger is represented by an integer that is a power of 2: OnInsert - 1 OnModify - 2 OnDelete - 4 OnRname - 8 Sum the values for the desired triggers and return that sum. For example, to turn on OnInsert, OnModify, and OnDelete (but not OnRename) return the value 11 (1 + 2 + 8).
As the Change log only knows Insert,Modify and delete (not rename), Navision is always returning MODIFY + RENAME (ie. 2 + 8) if the changelog is set to MODIFY.
quote:
Sum the values for the desired triggers and return that sum. For example, to turn on OnInsert, OnModify, and OnDelete (but not OnRename) return the value 11 (1 + 2 + 8).
This calls Insert,Modify and rename trigger but not delete. To call Insert,Modify,Delete you need to send 1+2+4 = 7 !!! By the way, for checking NOTBLANK you need to check on INSERT and MODIFY, so in this case you need to send 3. Maybe you have to send some higher value if the standard Change log should not be interrupted.
This is all good in theory, but remember when Navision inserts a record…once the key fields are filled in (without changing the form to DelayedInsert). In this case any non-key fields are blank.
My two cents [;)] This is from our “Mandatory Fields” app.: Start Function MandatoryFieldsComplete(RecRef : RecordRef) Complete : Boolean MandFldLine.SETRANGE("Table No.", RecRef.NUMBER); IF NOT MandFldLine.FIND('-') THEN EXIT(TRUE); Complete := TRUE; REPEAT IF NOT FieldIsComplete(RecRef.FIELD(MandFldLine."Field No.")) THEN Complete := FALSE; UNTIL (MandFldLine.NEXT = 0) OR (NOT Complete); EXIT(Complete); End Function Start Function FieldIsComplete(FldRef : FieldRef) Complete : Boolean Complete := TRUE; IF (FORMAT(FldRef.CLASS) = 'FlowField') THEN FldRef.CALCFIELD; CASE FORMAT(FldRef.TYPE) OF 'Text', 'Code', 'DateFormula', 'RecordID': IF DELCHR(FORMAT(FldRef.VALUE), '=', ' ') = '' THEN Complete := FALSE; 'Option': IF FORMAT(FldRef.VALUE) IN ['', ' ', '0'] THEN Complete := FALSE; 'Decimal', 'Integer': IF FORMAT(FldRef.VALUE) IN ['', '0'] THEN Complete := FALSE; 'Date': IF FORMAT(FldRef.VALUE) IN ['', '0', '0D'] THEN Complete := FALSE; 'Time': IF FORMAT(FldRef.VALUE) IN ['', '0', '0T'] THEN Complete := FALSE; 'Boolean': IF FORMAT(FldRef.VALUE) IN ['', Text000] THEN Complete := FALSE; END; EXIT(Complete); End Function
You have a setup table MandFldLine, actually defining the fields that are mandatory (must be filled) for a table. You call the function MandatoryFieldsComplete by passing a RecordReference of the record you want to check and you receive a TRUE or FALSE if all mandatory fields are populated. Well, that’s just the core of this tiny module - it also provides some smart features - so, if this doesn’t help, please contact me [8D] Regards,
Hi Joerg, thats the basic principle that I had planned on following. The main thing I now need to investigate is the use of RecordRef and how its called and defined. Any suggestions on that one ??? [:D]
We call our function e.g. like this: RecRef.GETTABLE(Rec); Complete := MandFieldCheck.MandatoryFieldsComplete(RecRef);
For instance, this could be inserted into OnInsert, OnModify or OnQueryCloseForm, or anywhere in the code. Of course, the disadvantage is that these lines have to be implemented into every object where you want to do that check … or you could integrate this in the ChangeLog functions. Regards,