← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 8895: Improve program validation rules.

 

------------------------------------------------------------
revno: 8895
committer: Tran Chau <tran.hispvietnam@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2012-11-06 14:09:13 +0700
message:
  Improve program validation rules.
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationResult.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationService.java
  dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramValidationService.java
  dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java
  dhis-2/dhis-web/dhis-web-caseentry/src/main/resources/org/hisp/dhis/caseentry/i18n_module.properties
  dhis-2/dhis-web/dhis-web-caseentry/src/main/webapp/dhis-web-caseentry/validationResult.vm
  dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java
  dhis-2/dhis-web/dhis-web-light/src/main/webapp/dhis-web-light/namebased/programStageForm.vm


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationResult.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationResult.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationResult.java	2012-11-06 07:09:13 +0000
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2004-2009, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.hisp.dhis.program;
+
+/**
+ * @author Chau Thu Tran
+ * 
+ * @version ProgramValidationResult.java 10:33:59 AM Nov 6, 2012 $
+ */
+public class ProgramValidationResult
+{
+    private ProgramStageInstance programStageInstance;
+
+    private ProgramValidation programValidation;
+
+    private String leftsideValue;
+
+    private String rightsideValue;
+
+    // -------------------------------------------------------------------------
+    // Constructors
+    // -------------------------------------------------------------------------
+
+    public ProgramValidationResult()
+    {
+    }
+
+    public ProgramValidationResult( ProgramStageInstance programStageInstance, ProgramValidation programValidation,
+        String leftsideValue, String rightsideValue )
+    {
+        super();
+        this.programStageInstance = programStageInstance;
+        this.programValidation = programValidation;
+        this.leftsideValue = leftsideValue;
+        this.rightsideValue = rightsideValue;
+    }
+
+    // -------------------------------------------------------------------------
+    // Equals, hashCode and toString
+    // -------------------------------------------------------------------------
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((programStageInstance == null) ? 0 : programStageInstance.hashCode());
+        result = prime * result + ((programValidation == null) ? 0 : programValidation.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+            return true;
+        if ( obj == null )
+            return false;
+        if ( getClass() != obj.getClass() )
+            return false;
+        ProgramValidationResult other = (ProgramValidationResult) obj;
+        if ( programStageInstance == null )
+        {
+            if ( other.programStageInstance != null )
+                return false;
+        }
+        else if ( !programStageInstance.equals( other.programStageInstance ) )
+            return false;
+        if ( programValidation == null )
+        {
+            if ( other.programValidation != null )
+                return false;
+        }
+        else if ( !programValidation.equals( other.programValidation ) )
+            return false;
+        return true;
+    }
+
+    // -------------------------------------------------------------------------
+    // Setters && Getters
+    // -------------------------------------------------------------------------
+
+    public ProgramStageInstance getProgramStageInstance()
+    {
+        return programStageInstance;
+    }
+
+    public void setProgramStageInstance( ProgramStageInstance programStageInstance )
+    {
+        this.programStageInstance = programStageInstance;
+    }
+
+    public ProgramValidation getProgramValidation()
+    {
+        return programValidation;
+    }
+
+    public void setProgramValidation( ProgramValidation programValidation )
+    {
+        this.programValidation = programValidation;
+    }
+
+    public String getLeftsideValue()
+    {
+        return leftsideValue;
+    }
+
+    public void setLeftsideValue( String leftsideValue )
+    {
+        this.leftsideValue = leftsideValue;
+    }
+
+    public String getRightsideValue()
+    {
+        return rightsideValue;
+    }
+
+    public void setRightsideValue( String rightsideValue )
+    {
+        this.rightsideValue = rightsideValue;
+    }
+
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationService.java	2012-07-30 10:10:18 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramValidationService.java	2012-11-06 07:09:13 +0000
@@ -52,13 +52,13 @@
     Collection<ProgramValidation> getProgramValidation( Program program );
 
     Collection<ProgramValidation> getProgramValidation( Program program, Boolean dateType );
-    
+
     Collection<ProgramValidation> getProgramValidation( ProgramStage programStage );
 
     Collection<ProgramValidation> getProgramValidation( ProgramStageDataElement psdataElement );
 
-    boolean runValidation( ProgramValidation validation, ProgramStageInstance programStageInstance,
-        I18nFormat format );
-    
+    ProgramValidationResult runValidation( ProgramValidation validation,
+        ProgramStageInstance programStageInstance, I18nFormat format );
+
     String getValidationDescription( String condition );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramValidationService.java'
--- dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramValidationService.java	2012-10-18 09:58:59 +0000
+++ dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramValidationService.java	2012-11-06 07:09:13 +0000
@@ -67,6 +67,8 @@
         + SEPARATOR_ID + "[0-9]*]*)" + "\\]";
 
     private final String regExpComparator = "(<=|>=|==|!=|<|>|>)+";
+    
+    private final String SEPARATE_SIDE_VALUE = "&&";
 
     private final String INVALID_CONDITION = "Invalid condition";
 
@@ -136,24 +138,24 @@
     }
 
     @Override
-    public boolean runValidation( ProgramValidation validation, ProgramStageInstance programStageInstance,
-        I18nFormat format )
+    public ProgramValidationResult runValidation( ProgramValidation validation,
+        ProgramStageInstance programStageInstance, I18nFormat format )
     {
         if ( !validation.getDateType() )
         {
-            // ---------------------------------------------------------------------
-            // parse left-expressions
-            // ---------------------------------------------------------------------
-
-            boolean resultLeft = runExpression( validation.getLeftSide(), programStageInstance );
-
-            // ---------------------------------------------------------------------
-            // parse right-expressions
-            // ---------------------------------------------------------------------
-
-            boolean resultRight = runExpression( validation.getRightSide(), programStageInstance );
-
-            return (resultLeft == resultRight);
+            String resultLeft = runExpression( validation.getLeftSide(), programStageInstance );
+            String resultRight = runExpression( validation.getRightSide(), programStageInstance );
+
+            boolean validLeftSide = Boolean.parseBoolean( resultLeft.split( SEPARATE_SIDE_VALUE )[0] );
+            boolean validRightSide = Boolean.parseBoolean( resultRight.split( SEPARATE_SIDE_VALUE )[0] );
+            if ( validLeftSide == validRightSide )
+            {
+                return null;
+            }
+            else
+            {
+                return new ProgramValidationResult( programStageInstance, validation, resultLeft.split( SEPARATE_SIDE_VALUE )[1], resultRight.split( SEPARATE_SIDE_VALUE )[1] );
+            }
         }
 
         return runDateExpression( validation, programStageInstance, format );
@@ -191,11 +193,12 @@
         return result;
     }
 
-    public boolean runDateExpression( ProgramValidation programValidation, ProgramStageInstance programStageInstance,
-        I18nFormat format )
+    private ProgramValidationResult runDateExpression( ProgramValidation programValidation,
+        ProgramStageInstance programStageInstance, I18nFormat format )
     {
+        boolean valid = true;
+
         Pattern pattern = Pattern.compile( regExp );
-
         Matcher matcher = pattern.matcher( programValidation.getLeftSide() );
 
         if ( matcher.find() )
@@ -204,57 +207,83 @@
 
             PatientDataValue dataValue = getPatientDataValue( match, programStageInstance );
 
-            if ( dataValue == null )
-            {
-                return true;
-            }
-
-            String rightSide = programValidation.getRightSide();
-            Date dueDate = dataValue.getProgramStageInstance().getDueDate();
-            Date currentDate = dataValue.getTimestamp();
-            Date value = format.parseDate( dataValue.getValue() );
-
-            int index = rightSide.indexOf( 'D' );
-            if ( index < 0 )
-            {
-                int rightValidation = Integer.parseInt( rightSide );
-
-                switch ( rightValidation )
-                {
-                case BEFORE_CURRENT_DATE:
-                    return value.before( currentDate );
-                case BEFORE_OR_EQUALS_TO_CURRENT_DATE:
-                    return (value.before( currentDate ) || value.equals( currentDate ));
-                case AFTER_CURRENT_DATE:
-                    return value.after( currentDate );
-                case AFTER_OR_EQUALS_TO_CURRENT_DATE:
-                    return (value.after( currentDate ) || value.equals( currentDate ));
-                case BEFORE_DUE_DATE:
-                    return value.before( dueDate );
-                case BEFORE_OR_EQUALS_TO_DUE_DATE:
-                    return (value.before( dueDate ) || value.equals( dueDate ));
-                case AFTER_DUE_DATE:
-                    return value.after( dueDate );
-                case AFTER_OR_EQUALS_TO_DUE_DATE:
-                    return (value.after( dueDate ) || value.equals( dueDate ));
-                default:
-                    return true;
-                }
-            }
-
-            int rightValidation = Integer.parseInt( rightSide.substring( 0, index ) );
-
-            int daysValue = Integer.parseInt( rightSide.substring( index + 1, rightSide.length() ) );
-
-            if ( rightValidation == BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS )
-            {
-                long maxDays = dueDate.getTime() / 86400000 + daysValue;
-                long minDays = dueDate.getTime() / 86400000 - daysValue;
-                long valueDays = value.getTime() / 86400000;
-                return (valueDays <= maxDays && valueDays >= minDays);
+            if ( dataValue != null )
+            {
+
+                String rightSide = programValidation.getRightSide();
+                Date dueDate = dataValue.getProgramStageInstance().getDueDate();
+                Date currentDate = dataValue.getTimestamp();
+                Date value = format.parseDate( dataValue.getValue() );
+                String operator = "";
+
+                int index = rightSide.indexOf( 'D' );
+                if ( index < 0 )
+                {
+                    int rightValidation = Integer.parseInt( rightSide );
+
+                    switch ( rightValidation )
+                    {
+                    case BEFORE_CURRENT_DATE:
+                        operator = "<";
+                        valid = value.before( currentDate );
+                        break;
+                    case BEFORE_OR_EQUALS_TO_CURRENT_DATE:
+                        operator = "<=";
+                        valid = (value.before( currentDate ) || value.equals( currentDate ));
+                        break;
+                    case AFTER_CURRENT_DATE:
+                        operator = ">";
+                        valid = value.after( currentDate );
+                        break;
+                    case AFTER_OR_EQUALS_TO_CURRENT_DATE:
+                        operator = ">=";
+                        valid = (value.after( currentDate ) || value.equals( currentDate ));
+                        break;
+                    case BEFORE_DUE_DATE:
+                        operator = "<";
+                        currentDate = dueDate;
+                        valid = value.before( dueDate );
+                        break;
+                    case BEFORE_OR_EQUALS_TO_DUE_DATE:
+                        operator = "<=";
+                        currentDate = dueDate;
+                        valid = (value.before( dueDate ) || value.equals( dueDate ));
+                    case AFTER_DUE_DATE:
+                        operator = ">";
+                        currentDate = dueDate;
+                        valid = value.after( dueDate );
+                        break;
+                    case AFTER_OR_EQUALS_TO_DUE_DATE:
+                        operator = ">=";
+                        currentDate = dueDate;
+                        valid = (value.after( dueDate ) || value.equals( dueDate ));
+                        break;
+                    default:
+                        break;
+                    }
+                }
+
+                int rightValidation = Integer.parseInt( rightSide.substring( 0, index ) );
+
+                int daysValue = Integer.parseInt( rightSide.substring( index + 1, rightSide.length() ) );
+
+                if ( rightValidation == BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS )
+                {
+                    long maxDays = dueDate.getTime() / 86400000 + daysValue;
+                    long minDays = dueDate.getTime() / 86400000 - daysValue;
+                    long valueDays = value.getTime() / 86400000;
+                    valid = (valueDays <= maxDays && valueDays >= minDays);
+                }
+
+                if ( !valid )
+                {
+                    String result = dataValue + " " + operator + " " + format.formatDate( currentDate );
+                    return new ProgramValidationResult( programStageInstance, programValidation, result, null );
+                }
             }
         }
-        return true;
+
+        return null;
     }
 
     public Collection<ProgramValidation> getProgramValidation( ProgramStage programStage )
@@ -326,9 +355,8 @@
                 return INVALID_CONDITION;
             }
 
-            matcher.appendReplacement( description, "[" + programStage.getName() + SEPARATOR_ID + dataElement.getName()
-                + "]" );
-
+            matcher.appendReplacement( description, programStage.getName() + SEPARATOR_ID + dataElement.getName() );
+            
         }
 
         matcher.appendTail( description );
@@ -344,8 +372,9 @@
 
     private String NOT_NULL_VALUE_IN_EXPRESSION = "{NOT-NULL-VALUE}";
 
-    private boolean runExpression( String expression, ProgramStageInstance programStageInstance )
+    private String runExpression( String expression, ProgramStageInstance programStageInstance )
     {
+        boolean valid = true;
         String comparetor = "";
         Pattern pattern = Pattern.compile( regExpComparator );
         Matcher matcher = pattern.matcher( expression );
@@ -356,58 +385,43 @@
 
         String[] sides = expression.split( regExpComparator );
         String leftSideValue = getOneSideExpressionValue( sides[0].trim(), programStageInstance );
-        String rightSideValue = getOneSideExpressionValue( sides[1].trim(), programStageInstance );
-
+        String rightSideValue = getOneSideExpressionValue( sides[1].trim(), programStageInstance );        
         if ( leftSideValue == null || rightSideValue == null )
         {
-            return true;
+            return null;
         }
-        
+
         if ( expression.indexOf( SUM_OPERATOR_IN_EXPRESSION ) != -1 )
         {
             String result = leftSideValue + comparetor + rightSideValue;
             final JEP parser = new JEP();
             parser.parseExpression( result );
-            return (parser.getValue() == 1.0);
+            valid = (parser.getValue() == 1.0);
         }
         else
         {
             if ( rightSideValue.equals( NOT_NULL_VALUE_IN_EXPRESSION ) )
             {
-                return leftSideValue == null ? false : true;
-            }
-            else if ( comparetor.equals( "==" ) && leftSideValue.equals( rightSideValue ) )
-            {
-                return true; 
-            }
-            else if ( comparetor.equals( "<" ) && leftSideValue.compareTo( rightSideValue )<0 )
-            {
-                return true;
-            }
-            else if ( comparetor.equals( "<=" ) && 
-                (  leftSideValue.equals( rightSideValue ) || leftSideValue.compareTo( rightSideValue )<0 ))
-            {
-                return true;
-            }
-            else if ( comparetor.equals( ">" ) && leftSideValue.compareTo( rightSideValue )>0 )
-            {
-                return true;
-            }
-            else if ( comparetor.equals( ">=" ) &&                 
-                ( leftSideValue.equals( rightSideValue ) || leftSideValue.compareTo( rightSideValue )>0 ))
-            {
-                return true;
-            }
-            else if ( comparetor.equals( "!=" ) && !leftSideValue.equals( rightSideValue ) )
-            {
-                return true;
+                valid = !( leftSideValue == null );            
+            }
+            else if ( (comparetor.equals( "==" ) && leftSideValue.equals( rightSideValue ))
+                || (comparetor.equals( "<" ) && leftSideValue.compareTo( rightSideValue ) < 0)
+                || (comparetor.equals( "<=" ) && (leftSideValue.equals( rightSideValue ) || leftSideValue
+                    .compareTo( rightSideValue ) < 0))
+                || (comparetor.equals( ">" ) && leftSideValue.compareTo( rightSideValue ) > 0)
+                || (comparetor.equals( ">=" ) && (leftSideValue.equals( rightSideValue ) || leftSideValue
+                    .compareTo( rightSideValue ) > 0))
+                || (comparetor.equals( "!=" ) && !leftSideValue.equals( rightSideValue )) )
+            {
+                valid = true;
             }
             else
             {
-                return false;
+                valid = false;
             }
         }
 
+        return valid + SEPARATE_SIDE_VALUE + leftSideValue + " " + comparetor + " " + rightSideValue;
     }
 
     private String getOneSideExpressionValue( String expression, ProgramStageInstance programStageInstance )

=== modified file 'dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java'
--- dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java	2012-09-25 09:12:30 +0000
+++ dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java	2012-11-06 07:09:13 +0000
@@ -55,6 +55,7 @@
 import org.hisp.dhis.program.ProgramStageDataElement;
 import org.hisp.dhis.program.ProgramStageInstance;
 import org.hisp.dhis.program.ProgramValidation;
+import org.hisp.dhis.program.ProgramValidationResult;
 import org.hisp.dhis.program.ProgramValidationService;
 
 import com.opensymphony.xwork2.Action;
@@ -90,7 +91,7 @@
 
     private Map<DataElement, String> resultDEMultiStages;
 
-    private List<ProgramValidation> programValidations;
+    private List<ProgramValidationResult> programValidationResults;
 
     private Map<Integer, String> leftsideFormulaMap;
 
@@ -120,9 +121,9 @@
         return rightsideFormulaMap;
     }
 
-    public List<ProgramValidation> getProgramValidations()
+    public List<ProgramValidationResult> getProgramValidationResults()
     {
-        return programValidations;
+        return programValidationResults;
     }
 
     public void setProgramValidationService( ProgramValidationService programValidationService )
@@ -155,7 +156,7 @@
     {
         resultDEMultiStages = new HashMap<DataElement, String>();
 
-        programValidations = new ArrayList<ProgramValidation>();
+        programValidationResults = new ArrayList<ProgramValidationResult>();
 
         // ---------------------------------------------------------------------
         // Get selected objects
@@ -238,28 +239,28 @@
         {
             for ( ProgramValidation validation : validations )
             {
-                boolean valid = programValidationService.runValidation( validation, programStageInstance, format );
+                ProgramValidationResult validationResult = programValidationService.runValidation( validation, programStageInstance, format );
 
-                if ( !valid )
+                if ( validationResult != null )
                 {
-                    programValidations.add( validation );
+                    programValidationResults.add( validationResult );
                 }
             }
         }
 
-        if ( !programValidations.isEmpty() )
+        if ( !programValidationResults.isEmpty() )
         {
-            leftsideFormulaMap = new HashMap<Integer, String>( programValidations.size() );
-            rightsideFormulaMap = new HashMap<Integer, String>( programValidations.size() );
+            leftsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
+            rightsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
 
-            for ( ProgramValidation validation : programValidations )
+            for ( ProgramValidationResult validationResult : programValidationResults )
             {
-                leftsideFormulaMap.put( validation.getId(),
-                    programValidationService.getValidationDescription( validation.getLeftSide() ) );
+                leftsideFormulaMap.put( validationResult.getProgramValidation().getId(),
+                    programValidationService.getValidationDescription( validationResult.getProgramValidation().getLeftSide() ) );
 
-                if ( validation.getDateType() )
+                if ( validationResult.getProgramValidation().getDateType() )
                 {
-                    String rightSide = validation.getRightSide();
+                    String rightSide = validationResult.getProgramValidation().getRightSide();
                     int index = rightSide.indexOf( 'D' );
                     if ( index < 0 )
                     {
@@ -268,35 +269,35 @@
                         switch ( rightValidation )
                         {
                         case BEFORE_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "before_current_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "before_current_date" ) );
                             break;
                         case BEFORE_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(),
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
                                 i18n.getString( "before_or_equals_to_current_date" ) );
                             break;
                         case AFTER_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "after_current_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "after_current_date" ) );
                             break;
                         case AFTER_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(),
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
                                 i18n.getString( "after_or_equals_to_current_date" ) );
                             break;
                         case BEFORE_DUE_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "before_due_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "before_due_date" ) );
                             break;
                         case BEFORE_OR_EQUALS_TO_DUE_DATE:
-                            rightsideFormulaMap.put( validation.getId(),
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
                                 i18n.getString( "before_or_equals_to_due_date" ) );
                             break;
                         case AFTER_DUE_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "after_due_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "after_due_date" ) );
                             break;
                         case AFTER_OR_EQUALS_TO_DUE_DATE:
                             rightsideFormulaMap
-                                .put( validation.getId(), i18n.getString( "after_or_equals_to_due_date" ) );
+                                .put( validationResult.getProgramValidation().getId(), i18n.getString( "after_or_equals_to_due_date" ) );
                             break;
                         default:
-                            rightsideFormulaMap.put( validation.getId(), "" );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
                             break;
 
                         }
@@ -310,20 +311,20 @@
                         if ( rightValidation == BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS )
                         {
                             rightsideFormulaMap.put(
-                                validation.getId(),
+                                validationResult.getProgramValidation().getId(),
                                 i18n.getString( "in_range_due_date_plus_or_minus" ) + " " + daysValue
                                     + i18n.getString( "days" ) );
                         }
                     }
                 }
-                else if ( validation.getRightSide().equals( "1==1" ) )
+                else if ( validationResult.getProgramValidation().getRightSide().equals( "1==1" ) )
                 {
-                    rightsideFormulaMap.put( validation.getId(), "" );
+                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
                 }
                 else
                 {
-                    rightsideFormulaMap.put( validation.getId(),
-                        programValidationService.getValidationDescription( validation.getRightSide() ) );
+                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
+                        programValidationService.getValidationDescription( validationResult.getProgramValidation().getRightSide() ) );
                 }
             }
         }

=== modified file 'dhis-2/dhis-web/dhis-web-caseentry/src/main/resources/org/hisp/dhis/caseentry/i18n_module.properties'
--- dhis-2/dhis-web/dhis-web-caseentry/src/main/resources/org/hisp/dhis/caseentry/i18n_module.properties	2012-11-02 04:25:31 +0000
+++ dhis-2/dhis-web/dhis-web-caseentry/src/main/resources/org/hisp/dhis/caseentry/i18n_module.properties	2012-11-06 07:09:13 +0000
@@ -96,7 +96,7 @@
 successful_validation=The data entry screen successfully passed validation
 unsuccessful_validation=The data entry screen has validation errors, please correct these before proceeding
 the_following_dataelements_are_in_multi_stages=The following data elements are in multi-program stages
-validation_result=Validation result
+validation_result=Validation Result
 should=should
 run_validation=Run validation
 please_select_village=Please select village
@@ -466,4 +466,7 @@
 single_event_with_registration_management = Single event with registration management
 multiple_individual_records_management = Multiple individual records management
 manual_person_aggregation_form = Manual person aggregation
-events = events
\ No newline at end of file
+events = events
+left_value = Left value
+right_value = Right value
+validation_rule = Validation rule
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-caseentry/src/main/webapp/dhis-web-caseentry/validationResult.vm'
--- dhis-2/dhis-web/dhis-web-caseentry/src/main/webapp/dhis-web-caseentry/validationResult.vm	2012-01-13 02:14:28 +0000
+++ dhis-2/dhis-web/dhis-web-caseentry/src/main/webapp/dhis-web-caseentry/validationResult.vm	2012-11-06 07:09:13 +0000
@@ -1,25 +1,40 @@
-<h3>$encoder.htmlEncode( $i18n.getString( "validation_result" ) )</h3>
+<h3>$encoder.htmlEncode( $i18n.getString( "validation_result" ) ) &nbsp;
+	#if($programValidationResults.size()==0)
+		<img src="../images/success_small.png">
+	#else
+		<img src="../images/warning_small.png">
+	#end
+</h3>
 
-#if( $programValidations.size()==0 )  
+#if( $programValidationResults.size()==0 )  
     <p class="bold">$encoder.htmlEncode( $i18n.getString( "successful_validation" ) )</p>
 #else
     <p class="bold">$encoder.htmlEncode( $i18n.getString( "unsuccessful_validation" ) )</p>
 
 	<p class="bold">$encoder.htmlEncode( $i18n.getString( "the_following_dataelements_are_in_multi_stages" ) )</p>
-	<table class="listTable" style="width:100%">
-		<tr>
-			<th>$i18n.getString( "description" )</th>
-			<th>$i18n.getString( "leftSide" )</th>
-			<th>$i18n.getString( "rightSide" )</th>
-		</tr>
+	<table class="listTable" id="multiValidation" name="multiValidation" style="width:100%">
+		<thead>
+			<tr>
+				<th>$i18n.getString( "validation_rule" )</th>                
+				<th>$i18n.getString( "description" )</th>
+				<th>$i18n.getString( "left_value" )</th>
+				<th>$i18n.getString( "right_value" )</th>
+				<th>$i18n.getString( "description" )</th>
+			</tr>
+		</thead>
 		#set( $mark = false )	    
-		#foreach( $programValidation in $programValidations ) 
-			<tr #alternate( $mark )>	    
-				<td>$programValidation.description</td>
-				<td>$leftsideFormulaMap.get( $programValidation.id )</td>
-				<td style="height:32px">$rightsideFormulaMap.get( $programValidation.id )</td>	        
-			</tr>
-			#set( $mark = !$mark )
-		#end
+		<tbody>
+			#foreach( $validationResult in $programValidationResults )
+				<tr #alternate( $mark )>	    
+					<td>$validationResult.programValidation.description</td>
+					<td>$leftsideFormulaMap.get( $validationResult.programValidation.id )</td>
+					<td>$validationResult.leftsideValue</td>
+					#if( $validationResult.programValidation.rightSide!="1==1")
+						<td>$validationResult.rightsideValue</td>
+						<td>$rightsideFormulaMap.get( $validationResult.programValidation.id )</td>	        
+					#end
+				#set( $mark = !$mark )
+			#end
+		</tbody>
 	</table>
-#end
+#end
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java'
--- dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java	2012-10-23 07:56:15 +0000
+++ dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java	2012-11-06 07:09:13 +0000
@@ -66,6 +66,7 @@
 import org.hisp.dhis.program.ProgramStageSectionService;
 import org.hisp.dhis.program.ProgramStageService;
 import org.hisp.dhis.program.ProgramValidation;
+import org.hisp.dhis.program.ProgramValidationResult;
 import org.hisp.dhis.program.ProgramValidationService;
 import org.hisp.dhis.util.ContextUtils;
 import javax.servlet.http.HttpServletRequest;
@@ -81,7 +82,7 @@
     private static final String SUCCESS_AND_BACK_TO_PROGRAMSTAGE = "success_back_to_programStage";
 
     private static final String REGISTER_NEXT_DUEDATE = "register_next_duedate";
-    
+
     private static final String SUCCESS_AND_BACK_TO_PROGRAMSTAGE_SECTION = "success_back_to_programStageSection";
 
     // -------------------------------------------------------------------------
@@ -204,9 +205,9 @@
     {
         this.programStageInstanceService = programStageInstanceService;
     }
-    
+
     private ProgramStageSectionService programStageSectionService;
-    
+
     public void setProgramStageSectionService( ProgramStageSectionService programStageSectionService )
     {
         this.programStageSectionService = programStageSectionService;
@@ -390,7 +391,7 @@
     {
         this.rightsideFormulaMap = rightsideFormulaMap;
     }
-    
+
     private Integer programStageSectionId;
 
     public void setProgramStageSectionId( Integer programStageSectionId )
@@ -402,14 +403,14 @@
     {
         return programStageSectionId;
     }
-    
+
     public ProgramStageSection programStageSection;
 
     public ProgramStageSection getProgramStageSection()
     {
         return programStageSection;
     }
-    
+
     private Boolean validated;
 
     public void setValidated( Boolean validated )
@@ -426,6 +427,13 @@
 
     private I18nFormat format;
 
+    private List<ProgramValidationResult> programValidationResults;
+    
+    public List<ProgramValidationResult> getProgramValidationResults()
+    {
+        return programValidationResults;
+    }
+    
     @Override
     public String execute()
         throws Exception
@@ -444,12 +452,13 @@
         org.hisp.dhis.program.ProgramStage dhisProgramStage = programStageService.getProgramStage( programStageId );
 
         patient = patientService.getPatient( patientId );
-        if( programStageSectionId != null && programStageSectionId != 0 )
+        if ( programStageSectionId != null && programStageSectionId != 0 )
         {
             this.programStageSection = programStageSectionService.getProgramStageSection( this.programStageSectionId );
-            
-            List<ProgramStageDataElement> listOfProgramStageDataElement = programStageSection.getProgramStageDataElements();
-            
+
+            List<ProgramStageDataElement> listOfProgramStageDataElement = programStageSection
+                .getProgramStageDataElements();
+
             dataElements = util.transformDataElementsToMobileModel( listOfProgramStageDataElement );
         }
         else
@@ -548,14 +557,14 @@
         {
             return REGISTER_NEXT_DUEDATE;
         }
-        
+
         validated = true;
-        
+
         if ( programStageSectionId != null && programStageSectionId != 0 )
         {
             return SUCCESS_AND_BACK_TO_PROGRAMSTAGE_SECTION;
         }
-        
+
         if ( orgUnitId != 0 )
         {
             return SUCCESS;
@@ -573,28 +582,29 @@
         {
             for ( ProgramValidation validation : validations )
             {
-                boolean valid = programValidationService.runValidation( validation, programStageInstance, format );
-                if ( !valid )
+                ProgramValidationResult validationResult = programValidationService.runValidation( validation,
+                    programStageInstance, format );
+
+                if ( validationResult != null )
                 {
-                    programValidations.add( validation );
-                    validation.getDescription();
+                    programValidationResults.add( validationResult );
                 }
             }
         }
-        
-        if ( !programValidations.isEmpty() )
+
+        if ( !programValidationResults.isEmpty() )
         {
-            leftsideFormulaMap = new HashMap<Integer, String>( programValidations.size() );
-            rightsideFormulaMap = new HashMap<Integer, String>( programValidations.size() );
+            leftsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
+            rightsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
 
-            for ( ProgramValidation validation : programValidations )
+            for ( ProgramValidationResult validationResult : programValidationResults )
             {
-                leftsideFormulaMap.put( validation.getId(),
-                    programValidationService.getValidationDescription( validation.getLeftSide() ) );
+                leftsideFormulaMap.put( validationResult.getProgramValidation().getId(), programValidationService
+                    .getValidationDescription( validationResult.getProgramValidation().getLeftSide() ) );
 
-                if ( validation.getDateType() )
+                if ( validationResult.getProgramValidation().getDateType() )
                 {
-                    String rightSide = validation.getRightSide();
+                    String rightSide = validationResult.getProgramValidation().getRightSide();
                     int index = rightSide.indexOf( 'D' );
                     if ( index < 0 )
                     {
@@ -603,35 +613,39 @@
                         switch ( rightValidation )
                         {
                         case BEFORE_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "before_current_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
+                                i18n.getString( "before_current_date" ) );
                             break;
                         case BEFORE_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(),
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
                                 i18n.getString( "before_or_equals_to_current_date" ) );
                             break;
                         case AFTER_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "after_current_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
+                                i18n.getString( "after_current_date" ) );
                             break;
                         case AFTER_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validation.getId(),
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
                                 i18n.getString( "after_or_equals_to_current_date" ) );
                             break;
                         case BEFORE_DUE_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "before_due_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
+                                i18n.getString( "before_due_date" ) );
                             break;
                         case BEFORE_OR_EQUALS_TO_DUE_DATE:
-                            rightsideFormulaMap.put( validation.getId(),
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
                                 i18n.getString( "before_or_equals_to_due_date" ) );
                             break;
                         case AFTER_DUE_DATE:
-                            rightsideFormulaMap.put( validation.getId(), i18n.getString( "after_due_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
+                                i18n.getString( "after_due_date" ) );
                             break;
                         case AFTER_OR_EQUALS_TO_DUE_DATE:
-                            rightsideFormulaMap
-                                .put( validation.getId(), i18n.getString( "after_or_equals_to_due_date" ) );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
+                                i18n.getString( "after_or_equals_to_due_date" ) );
                             break;
                         default:
-                            rightsideFormulaMap.put( validation.getId(), "" );
+                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
                             break;
 
                         }
@@ -645,20 +659,20 @@
                         if ( rightValidation == BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS )
                         {
                             rightsideFormulaMap.put(
-                                validation.getId(),
+                                validationResult.getProgramValidation().getId(),
                                 i18n.getString( "in_range_due_date_plus_or_minus" ) + " " + daysValue
                                     + i18n.getString( "days" ) );
                         }
                     }
                 }
-                else if ( validation.getRightSide().equals( "1==1" ) )
+                else if ( validationResult.getProgramValidation().getRightSide().equals( "1==1" ) )
                 {
-                    rightsideFormulaMap.put( validation.getId(), "" );
+                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
                 }
                 else
                 {
-                    rightsideFormulaMap.put( validation.getId(),
-                        programValidationService.getValidationDescription( validation.getRightSide() ) );
+                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), programValidationService
+                        .getValidationDescription( validationResult.getProgramValidation().getRightSide() ) );
                 }
             }
         }

=== modified file 'dhis-2/dhis-web/dhis-web-light/src/main/webapp/dhis-web-light/namebased/programStageForm.vm'
--- dhis-2/dhis-web/dhis-web-light/src/main/webapp/dhis-web-light/namebased/programStageForm.vm	2012-10-25 08:59:17 +0000
+++ dhis-2/dhis-web/dhis-web-light/src/main/webapp/dhis-web-light/namebased/programStageForm.vm	2012-11-06 07:09:13 +0000
@@ -8,15 +8,15 @@
 	</p>
 </div>
 #set( $typeViolationsSize = $typeViolations.size() )
-#set( $programViolationsSize = $programValidations.size() )
+#set( $programViolationsSize = $programValidationResults.size() )
 
 #if( $typeViolationsSize > 0 || $programViolationsSize > 0)
 <div class="header-box" align="center">
 	<h3 style="text-align: left; background-color: #990000; color: white;">$i18n.getString("warnings_and_errors")</h3>
 	<p style="text-align: left;">
 		#if ($typeViolationsSize > 0)$typeViolationsSize $i18n.getString("type_violation_errors"). <br /> #end
-		#foreach($programValidation in $programValidations)
-			$programValidation.description. <br/>
+		#foreach($programValidation in $programValidationResults)
+			$programValidation.programValidation.description . <br/>
 		#end
 		#if ($programViolationsSize > 0 && $typeViolationsSize == 0) <a href="showPatientProgramList.action?patientId=${patientId}">$i18n.getString("save_any_way")</a> #end	
 	</p>