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.
Interface- ModelValidator
ClassName- ModelValidatorVariables
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 |
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
First word of namespace must be Assembly Name in which class is written.
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.
Notify when model change happened like new, update and delete actions for additional validation
Notify when doctype action happened like PrepareIt and CompleteIt functions
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)
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);
}
}
}
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
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);
}
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 |
1 | Before Save |
2 | AfterSave |
3 | BeforeDelete |
4 | AfterDelete |
5 | PrepareIt |
6 | CompleteIt |
7 | ApproveIt |
8 | 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
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);
}
}
}
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.
/* 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 "";
}
}
}