How to validate a table relation in C/AL code?

frisch
Member Posts: 20
I'm running into an issue with validating tables, and hope someone here has an idea about how to solve this.
Example:
In table Sales Header (36), there's a field Shipping Agent Service Code (5794), which has a complicated relation to the Shipping Agent Services (5790) table:
I started by running a TESTFIELD, but this only checks if the field is populated. It does not check the contents of the field against the table relation!
My first idea to solve this was try to VALIDATE in this situation, but VALIDATE does the following:
- check the table relation;
- run code in OnValidate trigger.
However, the OnValidate trigger for this field contains this line:
I don't give up thát quick, so I built a little function that checks the Field table and picks up TableRelation and FieldRelation.
This works for most fields, but if you do that for Shipping Agent Service Code, you will see that both are set to 0
Actually, this is the case for all complicated relations.
Question: How can I validate the contents of the field in table 36 against the table relation, without running the OnValidate trigger?
If this is not possible by standard, is there a way to retrieve the complicated table relation and either parse this or load it into a filter?
Example:
In table Sales Header (36), there's a field Shipping Agent Service Code (5794), which has a complicated relation to the Shipping Agent Services (5790) table:
"Shipping Agent Services".Code WHERE (Shipping Agent Code=FIELD(Shipping Agent Code))In my requirements, I have to check the contents of this field before running code that requires my Sales Header to be Status::Released.
I started by running a TESTFIELD, but this only checks if the field is populated. It does not check the contents of the field against the table relation!
My first idea to solve this was try to VALIDATE in this situation, but VALIDATE does the following:
- check the table relation;
- run code in OnValidate trigger.
However, the OnValidate trigger for this field contains this line:
Shipping Agent Service Code - OnValidate() TESTFIELD(Status,Status::Open);Hmm... so the code I'm running requires Status to be Released, and the OnValidate trigger requires it to be Open. Problem!
I don't give up thát quick, so I built a little function that checks the Field table and picks up TableRelation and FieldRelation.
This works for most fields, but if you do that for Shipping Agent Service Code, you will see that both are set to 0

Question: How can I validate the contents of the field in table 36 against the table relation, without running the OnValidate trigger?
If this is not possible by standard, is there a way to retrieve the complicated table relation and either parse this or load it into a filter?
0
Best Answer
-
Hmmm, just had a look at the code in 2016 and I don't particularly like how they are validating the relation (calling COMMIT followed if an IF CODEUNIT.RUN())
I'd suggest having a look at the virtual table 2000000141 "Table Relations Metadata" - it basically gives to all the info you enter when setting up table relations - even complex ones with filters and relations to multiple tables etc... (like "No." field on Sales Line for example).5
Answers
-
I am not sure that I get you completed, but does this make sense -
You can have something like -
ShippingAgentServices.SETRANGE("Shipping Agent Code",SalesHeader."Shipping Agent Code");
IF NOT ShippingAgentServices.FINDFIRST THEN
//NOT FOUND
ELSE
//FOUND
Where -
ShippingAgentServices = Rec of Table 5790.
SalesHeader = Rec of Table 36.
Thanks & Regards,
Saurav Dhyani
Do you Know this About NAV?
Connect - Twitter | Facebook | Google + | YouTube
Follow - Blog | Facebook Page | Google + Page0 -
The table relation that you show is not that complicated in the grand scheme of the NAV world - there are many other table relations that are much more complex. That being said, Suarav gave an example that is something you should keep in your pocket of "Nav things to know" - just look at the table relation and translate it into SETRANGE or SETFILTER commands and execute the same "validation" as in the TableRelation property.
One thing to mention however - if the field is populated, then it MUST have passed the TableRelation. So any value in that field WILL appear in the the Shipping Agent Services Table - with one exception. It IS possible to DELETE a value in the Shipping Agent Services table AFTER it has been entered on a Sales Header - there is no checking upon DELETE in that table.
The TESTFIELD(Status,Status::Open) in the OnValidate trigger is enforcing a business rule that you can't change the Shipping Agent Services Code if the order has been released - you need to Reopen the order before changing. This, in my opinion is a pretty good business rule - a common reason for this might be if you change the services from "Next Day" to "overnight", you might want to change the shipping charge.
So use Suarav's code as a template, and do other checks/code as necessary in the "found" and "not found" blocks.
Good luck!
RonRon2 -
If I understand correctly you just want to validate that the "Shipping Agent Service Code" is valid? Assuming this is the case then all you need to do is...
IF NOT ShippingAgentService.GET(SalesHeader."Shipping Agent Code",SalesHeader."Shipping Agent Service Code") THEN ERROR('Invalid Shipping Agent Code')
...hopefully the variables are self-explanatory, also good practice to use TextConstants instead of hard coded text strings0 -
First things first, thanks for all your responses!
However, it's a bit more complicated than it looks.
About the relation: If I remember right from my database lessons, a table relation with filters was called a "complicated relation". Then again, I was in that class in 2000 and back then I like to party, so correct me if I'm wrong
Basically, what I built is a generic function which I can call from anywhere, similar to VALIDATE("Shipping Agent Service Code"). I built it as generic as possible, so I can basically throw any record variable at it that I want to. Like this:MyCodeunit.IsTableRelationOK("Sales Header", 'Shipping Agent Service Code', TRUE);
Where "Sales Header" ends up in a variant that is handled into a RecordRef (or accepted as a RecordRef, if a RecordRef is sent in).
Shipping Agent Service Code is also sent into a variant, which is checked for being either Integer, Text or FieldRef.
The TRUE is always a boolean, which determines if I want the function to either throw an error or just return a boolean answering the "Is Table Relation OK?" question.
The function works fine as long as the relation is simple, and it gets a TableRelation and FieldRelation integer. I also want it to check the complicated relation as described, and that's my challenge...
Reasons I want to do it this way are:
- My code is much clearer and more compact when I call MyCodeunit.IsTableRelationOK, than it is to call all SETRANGE and FINDFIRST or FINDSET commands every time I need to check a certain relation;
- Generic functions might save me time in the future (when I need something similar);
- If Microsoft (or someone else) ever decides to change the relation, my code won't fail but will follow;
0 -
Ah generic relationship validation...if only you'd said at the start
Have a look at how standard NAV does it - take a look at codeunit 8611 "Config Package Management" - I'm sure you'll find it interesting1 -
Hmmm, just had a look at the code in 2016 and I don't particularly like how they are validating the relation (calling COMMIT followed if an IF CODEUNIT.RUN())
I'd suggest having a look at the virtual table 2000000141 "Table Relations Metadata" - it basically gives to all the info you enter when setting up table relations - even complex ones with filters and relations to multiple tables etc... (like "No." field on Sales Line for example).5 -
One thing to mention however - if the field is populated, then it MUST have passed the TableRelation. So any value in that field WILL appear in the the Shipping Agent Services Table - with one exception. It IS possible to DELETE a value in the Shipping Agent Services table AFTER it has been entered on a Sales Header - there is no checking upon DELETE in that table.Hmmm, just had a look at the code in 2016 and I don't particularly like how they are validating the relation (calling COMMIT followed if an IF CODEUNIT.RUN())I'd suggest having a look at the virtual table 2000000141 "Table Relations Metadata" - it basically gives to all the info you enter when setting up table relations - even complex ones with filters and relations to multiple tables etc... (like "No." field on Sales Line for example).0
-
... took a look at codeunits around the 8600 range. They use VALIDATE to check the contents of a field.
So generic relation validation is only really possible from NAV 2016.
Funny to see though how similar my code is to the GetRelationInfoByIDs function in cu 86170
Categories
- All Categories
- 73 General
- 73 Announcements
- 66.6K Microsoft Dynamics NAV
- 18.7K NAV Three Tier
- 38.4K NAV/Navision Classic Client
- 3.6K Navision Attain
- 2.4K Navision Financials
- 116 Navision DOS
- 851 Navision e-Commerce
- 1K NAV Tips & Tricks
- 772 NAV Dutch speaking only
- 617 NAV Courses, Exams & Certification
- 2K Microsoft Dynamics-Other
- 1.5K Dynamics AX
- 320 Dynamics CRM
- 111 Dynamics GP
- 10 Dynamics SL
- 1.5K Other
- 990 SQL General
- 383 SQL Performance
- 34 SQL Tips & Tricks
- 35 Design Patterns (General & Best Practices)
- 1 Architectural Patterns
- 10 Design Patterns
- 5 Implementation Patterns
- 53 3rd Party Products, Services & Events
- 1.6K General
- 1.1K General Chat
- 1.6K Website
- 83 Testing
- 1.2K Download section
- 23 How Tos section
- 252 Feedback
- 12 NAV TechDays 2013 Sessions
- 13 NAV TechDays 2012 Sessions