Model Validator

Overview  

By introducing this functionality, system addresses the need of write additional or custom requirements for Model classes’ existing functionality.  In Many cases, Developer needs to change /override existing logic or want to put additional validation check on some or all Mclasses actions then he/she can use this functionality to customize logic according to client needs.  

   

In order to achieve this functionality, we have introduced a new interface (Model Validation).  This document is written for developer(s) that helps them to understand the Model Validation interface and how he can write Model Validator class according to their need and where he/she need to register Model Validator class.  

Product Version and dependency

This feature is supported by 'Vienna Advantage Framework <5.0.1.0>'  

How to Use

Introduction

Before jump to write user defined Model validator class first we need to know a class and interface that are used in or by ModelValidator class. 

  1. Interface- ModelValidator 

  2. ClassName- ModelValidatorVariables 

   

  1. ModelValidator

Function Name 

Description 

void Initialize(ModelValidationEngine engine, int ClientId); 

Called by Base Validator engine to register model classes 

Void BindModelAction(PO po) 

Called By PO class to bind Model Actions class to override base Model Class 

int GetAD_Client_ID();

 

Return client id passed in initialize function 

String Login(int AD_Org_ID, int AD_Role_ID, int AD_User_ID); 

 Called On when User logged in 

String ModelChange(PO po, int changeType); 

Any change in Model data record  

Supported change type (New,Update,Delete) 

String DocValidate(PO po, int docTiming);

 

Doc Action Specific Listener. Two timing supported. 1. OnPrepare 2. AfterComplete 

   

  1. ModelValidatorVariables 

Following table consist ModelValidator Variables of Events, fired By ModelValidatorEngine. It defines the action type that will be passed to ModelChange or DocValidate methods of user defined model validator class. 

Variable 

Uses 

Const int CHANGETYPE_NEW 

New record type in Model Change Listener 

Const int CHANGETYPE_UPDATE  

Update record type variable in Model change Listener 

Const int CHANGETYPE_DELETE 

Delete type parameter in Model Change listener 

Const int DOCTIMING_BEFORE_PREPARE 

Parameter for prepare DocAction in DocValidate Listener 

Const DOCTIMING_AFTER_COMPLETE 

Parameter for complete DocAction in DocValidate Listener 

 

First three Variables are handled in PO class and PO invoke ModelValidationEngine to fire 

ModelChanged event to notify registered client’s ModelValidator classes.  

The sequence of ModelChanged event is After the BEFORE SAVE/DELETE of Model Class but Before the actual SAVE and DELETE event of Model class. 

  

And Last two variables and Events are Written in almost all Existing M(Model) Classes’ PrepareIt and CompleteIt functions to perform additional validations and checks. 

  

e.g. In MOrder Class’ PrepareIt() function you can find following lines 

  

processMsg = ModelValidationEngine.Get().FireDocValidate(this, ModalValidatorVariables.DOCTIMING_BEFORE_PREPARE); 

 

if (_processMsg != null) 

return DocActionVariables.STATUS_INVALID; 

  

Note: if ModelValidator class return any string message then it is considered as error message. 

   

Implementation 

Developer needs to write a Model Validator class in Module or ViennaAdvantage assembly.  Keep in mind following points while writing the class 

  

  1. First word of namespace must be Assembly Name in which class is written.  

  2. Class must inherit from ModelValidator Interface.  

  

Note: Remove/delete “throw error exception” default implementation of ModelValidator interface’s method. Return suitable value (default null or blank string) form Interface methods according to return type of Methods. 

   

Let’s start to write a Model Validator class. 

Suppose developer want to customize MOrder for a Client MotoCorp in Module Moto Base Module having Prefix MOTO1_.  Assembly Name is MOTO1. And Validator class name is Moto1ModelValidator. 

  

Then class namespace must be MOTO1.”FolderName”.MotoModelValidator. Class name can be anything according to requirements should end with ModelValidator suffix. 

   

Following code sample is for MOTO1ModelValodaotr class. 

  

namespace MOTO1.Model
{
    class MOTO1ModelValidator : ModelValidator
    { 

      int AD_Client_ID = 0; // local variable to store client id
       public int GetAD_Client_ID()
       {
           return AD_Client_ID;
       }
       public void Initialize(ModelValidationEngine engine, int ClientId)
       {
           AD_Client_ID = ClientId; 

       } 

       public void BindModelAction(PO po)
        {
        } 
       /*Event listner*/
        public string ModelChange(PO po, int changeType)
        {
        }
        public string DocValidate(PO po, int docTiming)
        {
        } 

  

There are broadly three use cases scenario Where ModelValidator helps to customize Model Class. 

  

  1. Notify when model change happened like new, update and delete actions for additional validation   

  2. Notify when doctype action happened like PrepareIt and CompleteIt functions  

  3. Option to Bind ModelActions Likes before save/delete and after delete/save and DocActions(completeIt, PrepareIt, VoidIt, ApproveIt) with Option to skip base functionality or not. 

The Initialize function is most important block of ModelValidator class where registration is done for a model class for customization. As we discuss above, mainly three types of registration are done here. 

 

Following table shows relation between registration type and corresponding event handler/listener. 

Type 

Handler/Function 

Description 

engine.AddModelChange(X_C_Order.Table_Name, this); 

public string ModelChange(PO po, int changeType)

 

Fired When New,Update or delete is happened 

engine.AddDocValidate(X_C_Order.Table_Name, this); 

public string DocValidate(PO po, int docTiming)

 

Fired for Docaction Prepare And complete 

engine.AddModelAction(X_C_Order.Table_Name, this); 

public void BindModelAction(PO po)

 

Inject ModelAction class for Modelactions* 

  

*ModelAction Class is abstract class that is used by Client specific Model class to write client specific logic with Option to skip base functionality.  

  

We will cover all three case one by one with example. 

 

Case 1: Additional Validation on Model change (New,Update,Delete) 

 

  1. Add model change for Table. We consider MOrder here  

namespace MOTO1.Model
{
    class MOTO1ModelValidator : ModelValidator
    { 

public void Initialize(ModelValidationEngine engine, int ClientId)
    {
                AD_Client_ID = ClientId; 

                        engine.AddModelChange(X_C_Order.Table_Name, this); 

            } 

    } 

 

  1. Write Your Validation code in ModelChange Listener 

namespace MOTO1.Model
{
    class MOTO1ModelValidator : ModelValidator
    { 

         public string ModelChange(PO po, int changeType)
          { 

    if (po is MOrder)
     {
           if (ModalValidatorVariables.CHANGETYPE_NEW== changeType)
           {
                MOrder ord = (MOrder)po; 

               //Validation Logic
              return ""; // error string if validation failed
           }
           if (ModalValidatorVariables.CHANGETYPE_CHANGE== changeType)
           {
                MOrder ord = (MOrder)po; 

                //Logic
                 return "";
            }
           if (ModalValidatorVariables.CHANGETYPE_DELETE== changeType)
           {
                MOrder ord = (MOrder)po; 

               //Logic
                return "";
           } 

           } 

     } 

  

Case 2: Additional validation/task on before prepare and after complete 

  1. Add doc validation for Model class. E.g. MOrder 

            public void Initialize(ModelValidationEngine engine, int ClientId)
             {
                  AD_Client_ID = ClientId; 

                engine.AddDocValidate(X_C_Order.Table_Name, this); 

            }
 

  1. Write Validation code in DocValidate Listener/function 

public string DocValidate(PO po, int docTiming)
{
      if (po is MOrder)//Morder
      {
        if (docTiming == ModalValidatorVariables.DOCTIMING_BEFORE_PREPARE)
        {
           //logic  

          return ""; //error string if validation failed 

     }
      if (docTiming == ModalValidatorVariables.DOCTIMING_AFTER_COMPLETE) 
      { 

        //loigic 

        return "";
      } 

  } 

   

Case 3:  Inject ModelAction class to perform required customizable actions in M(Model) Class. 

In new class Developer either write all available functions of M(Model) Class or can write one or two functions depends on requirements. 

 

ModelAction is abstract class must inherited by Used defined Model Action class. Following actions are supported by ModelAction

 

Sr No 

Function Name 

Before Save 

AfterSave 

BeforeDelete 

AfterDelete 

PrepareIt 

CompleteIt 

ApproveIt 

VoidIt 

  •  Will add more DocActions as needed. 

First four functions are handled centrally in PO Class but to fire all other docAction type events we need to add some lines in corresponding DocActrions like we did in PrepareIt and Completeit functions in Case 2 but syntax is different. e.g., for PrepareIt function we need to write following lines In PrepareIt function of MOrder Class. 

 

if (this.ModelAction != null)
{
       bool skipBase; 


       bool ret = this.ModelAction.PrepareIt(out skipBase); 


       if (skipBase)  

       return ret; 

 

  

All above function has ‘skipBase’ parameter that will enable Developer to skip base/existing functionality in M(Model) Classes to run only customizable logic of ModelActions class. If Developer decide to run base functionality also then ModelActions class logic runs first followed by M(Model class) logic. 

 

We need to write a class inherited by ModelActions to change, add or override existing M(Model) class logic. 

 

For example, there is sample code for MotoMOrder that implement BeforeSave function/action for MOrder class. 

 

namespace MOTO1.Classes 

{ 

             public class MotoMOrder: ModelActions 

  { 

private MOrder mOrder; 

public MotoMOrder (PO order) 

mOrder = (MOrder)order; 

public override bool BeforeSave(bool newRecord, out bool skipBase) 

skipBase = true; // this out parameter value decide to skip MOrder's Before save logic or not  

retrun true; // if logic failed then return false 

             } 

 

After creating Model action class, we need to Bind it with Morder instance whenever it will get created. To achieve this follow the steps 

  

  1. Add(register) Model Action for Model Table. Here it is MOrder 

namespace MOTO1.Model
{
    class MOTO1ModelValidator : ModelValidator
    { 

   . 

   . 

   public void Initialize(ModelValidationEngine engine, int ClientId)
   {
               engine.AddModelAction(X_C_Order.Table_Name, this); 

   }  

               } 

 

  1. Inject Model action class in flowing function. It will Bind Model action with M(Class) instance. In Our case it is MOrder. 

public void BindModelAction(PO po)
{
         if (po is MOrder)
                   po.ModelAction = new MotoMOrder(po);  //Model Action Initialzed

 

So, when a record is inserted or updated then then BeforeSave function written in MotoMOrder class is called by system and customization logic is executed. 

 

Developer can write required Action(Model or Doc) to customization the existing logic and have option to skip the base logic of M(Model class) if needs to. 

  

Model Validator Class Binding 

Model validator works at client/Tenant level. Model Validator registered for one client cannot work or interferes with other client logics. 

Developer needs to enter fully qualified class name of ModelValidator class in “Model Validator Classes” field of Tenant window. 

Picture1.png

 

/* Complete Class Sample */ 

namespace MOTO1.Model
{
    class MOTOModelValidator : ModelValidator
    { 

      int AD_Client_ID = 0; // local variable to store client id
       public int GetAD_Client_ID()
       {
           return AD_Client_ID;
       }
       public void Initialize(ModelValidationEngine engine, int ClientId)
       {
           AD_Client_ID = ClientId; 

          engine.AddModelChange(X_C_Order.Table_Name, this); //Add Model Change for C_Order 

          engine.AddDocValidate(X_C_Order.Table_Name, this); //Add Doc validate events for C_Order 

          engine.AddModelAction(X_C_Order.Table_Name, this); //Add Model Actions for  C_Order 

       } 

       // Inject Model Action class In Model Instance  

       public void BindModelAction(PO po)
        { 

             if (po is MOrder)
                      po.ModelAction = new MotoMOrder(po);  //Model Action Initialized
        } 
      /*Event listner*/
        public string ModelChange(PO po, int changeType)
        { 

               if (po is MOrder) //Check for Morder instance
                {
                       if (ModalValidatorVariables.CHANGETYPE_NEW== changeType)
                       {
                                MOrder ord = (MOrder)po; 

                               //Validation Logic
                               return ""; // error string if validation failed
                       }
                       if (ModalValidatorVariables.CHANGETYPE_CHANGE== changeType)
                        {
                                MOrder ord = (MOrder)po; 

                               //Logic
                                return "";
                        }
                       if (ModalValidatorVariables.CHANGETYPE_DELETE== changeType)
                       {
                               MOrder ord = (MOrder)po; 

                               //Logic
                                return "";
                       } 

                } 

       }
        public string DocValidate(PO po, int docTiming)
        { 

     if (po is MOrder)//Morder
      {
                if (docTiming == ModalValidatorVariables.DOCTIMING_BEFORE_PREPARE)
                {
                          //logic  

                        return ""; //error string if validation failed 

               }
               if (docTiming == ModalValidatorVariables.DOCTIMING_AFTER_COMPLETE)
               { 

                        //loigic 

                        return "";
               } 

               } 

        }