Provide Check for Mandatory Fields

DouglasDouglas Member Posts: 10
edited 2015-09-23 in NAV Tips & Tricks
After struggling to find a succinct way to require certain fields on a card to be populated, I have come up with the following and it (so far) is working for me. I started to sense that NAV was not meant to have mandatory fields, but I need them for our business logic. Anyways, here we go...

Step One:
- We have a codeunit for various validation logic, in which I've added the function to read a custom table listing the tables and their fields that are mandatory. This function takes the table number, key field, and a "create mode". It returns a text "completion status" value. I find the table for the record I am validating. I loop through the mandatory fields, if the field is not populated, I add it to a list of incomplete fields. If the list of incomplete fields is empty, the completion status is "done". If the list of incomplete fields is populated, a message is displayed indicating the missing fields and allows the user an option to cancel the create of a new record or to stay on the (new or existing) record and enter the missing data, and the completion status is set to "delete" to cancel a create, or "return" to stay on the record. Logic follows:

CheckMadatoryFields(TableNumber : Integer;KeyField : Code[10];CreateMode : Boolean) Completion Status : Text[30]

// Read the 'LockoutFields' table to find the manditory fields for the table passed in.
LockoutFields.RESET;
LockoutFields.SETRANGE("Table No.", TableNumber);
LockoutFields.SETFILTER("Filter ID", 'MANDITORY_FIELD');

// Get a record reference for the table passed in
RecRef.OPEN(TableNumber);
RecRef.SETVIEW('WHERE("No." = FILTER(' + KeyField + '))');

// Set this to done, i.e. data is complete (don't delete by mistake).
CompletionStatus := 'done';

IF RecRef.FINDFIRST THEN BEGIN

// Check the record's manditory field(s) listed in the 'LockoutFields' table to see if they're blank.
IF LockoutFields.FINDSET THEN BEGIN
REPEAT
FldRef := RecRef.FIELD(LockoutFields."Field No.");

IF FORMAT(FldRef.VALUE) = '' THEN
FldList := FldList + ' - ' + FldRef.CAPTION + '\';

UNTIL LockoutFields.NEXT = 0;

END;

IF FldList <> '' THEN BEGIN
// If creating the record, add the 'Cancel Create' message, otherwise don't.
IF CreateMode THEN
DeleteRecord := CONFIRM(Text_ManditoryField + '\' + FldList + '\' + Text_CancelCreate, FALSE)
ELSE BEGIN
DeleteRecord := FALSE;
MESSAGE(Text_ManditoryField + '\' + FldList, FALSE);
END;

// Return a 'delete' status when deleting, or a 'return' status to stay on the record.
IF DeleteRecord THEN
CompletionStatus := 'delete'
ELSE
CompletionStatus := 'return';
END;
END;

RecRef.CLOSE;


Step 2:
- On the card you want to check for mandatory fields, in my case the Customer card, I added a function to call the validation function in the codeunit described above. I also added logic to the
OnQueryClosePage trigger to call my local function. This all worked fine, as the user could not close the Customer card without completing the mandatory fields or cancelling the create of the customer, except if the user were to use Ctrl+PgUp or Ctrl+PgDn, which took them off the record. The trick was putting the the correct logic in the OnNextRecord trigger so that the validation was executed and the Ctrl+PgUp or Ctrl+PgDn still functioned (note: I found this bit somewhere on mibuso, many thanks!). Logic follows:

OnNextRecord(...)
IF CheckManditoryFields = TRUE THEN BEGIN
Customer := Rec;
CurrentSteps := Customer.NEXT(Steps);
IF CurrentSteps <> 0 THEN
Rec := Customer;
EXIT(CurrentSteps);
END;

OnQueryClosePage(...)
EXIT(CheckManditoryFields);

CheckMandatoryFields() ExitValue : Boolean
// Check for manditory fields on this table. If there are missing manditory
// fields, the user can cancel this create, in which case, a 'TRUE' value is
// returned and we'll delete this record.

ExitValue := TRUE;

IF Rec."No." <> '' THEN BEGIN // This is blank if user quits immediately after page starts.
CompletionStatus := HHValidation.CheckManditoryFields(18,Rec."No.",CreateMode);

IF (CompletionStatus = 'delete')
AND (CreateMode = TRUE) THEN // User cancelled create (not edit), delete the record and exit.
Rec.DELETE(TRUE)
ELSE
IF CompletionStatus = 'done' THEN // User completed manditory fields, OK to exit.
ExitValue := TRUE
ELSE
ExitValue := FALSE; //User did not complete manditory fields and wants to return and add them.
END;

I think that's about it. The details of the custom table are really up to how you want to code it. The validation code unit can be what you want it to be. Using a table allows the mandatory fields to added or removed without changing any logic, and this "generic" validation logic could be put on any page. The key is the two triggers on the card and having a common validation routine to call.

Comments

  • SavatageSavatage Member Posts: 7,142
    We did it this way on our customer table.
    OnInsert()
    Blocked := TRUE;

    Blocked - OnValidate() //our mandatory fields
    TESTFIELD(Name);
    TESTFIELD(Address);
    TESTFIELD(City);
    TESTFIELD(State);
    TESTFIELD("ZIP Code");
    TESTFIELD("Country Code");
    TESTFIELD("Payment Terms Code");
    TESTFIELD("Price Group Code");
    TESTFIELD("Shipping Agent Code");
    TESTFIELD("Salesperson Code");

    All these fields have been set to NotBlank = Yes
    .. so they can't be cleared and left blank later on, a value must always exist

    I think this is alot easier and setting the logic on the TABLE insures that no matter how the data gets into the customer table the rules are the same.
  • DouglasDouglas Member Posts: 10
    I considered doing something like that, but I wanted the flexibility of making fields mandatory or not, without changing logic. Also, when the user leaves the customer card, all mandatory fields are checked and a list of all those that are missing info is listed once.
  • SavatageSavatage Member Posts: 7,142
    Well it looks like you put some time into it so good luck with your solution.
  • shogan@mila.ieshogan@mila.ie Member Posts: 113
    Harry - simple is usually the best way to start.

    11th hour on a simple form and as usual your posts come to the rescue!

    Thanks!
  • Tomas_CrespoTomas_Crespo Member Posts: 6
    We did it this way on our customer table.
    OnInsert()
    Blocked := TRUE;

    Blocked - OnValidate() //our mandatory fields
    TESTFIELD(Name);
    TESTFIELD(Address);
    TESTFIELD(City);
    TESTFIELD(State);
    TESTFIELD("ZIP Code");
    TESTFIELD("Country Code");
    TESTFIELD("Payment Terms Code");
    TESTFIELD("Price Group Code");
    TESTFIELD("Shipping Agent Code");
    TESTFIELD("Salesperson Code");

    Hi Harry, I'm interested in your solution.

    But, why do you block the customer? I think is to trigger OnValidate(), isn't it?
    But, do you unblock the customer after all the TESTFIELDs?

    Thanks

    PD: I'm using NAV2013, and for me is enough to ensure a field in page-lever (I don't need table level). Could it be accomplished by other trigger? I've tried with OnQueryClosePage, but it gives me a windows instead of the classical yellow-backgrounded error message, and don't allow "discard changes"
Sign In or Register to comment.