← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19798: tracker-capture: program validation criterias are not applied. user also has the option to procee...

 

------------------------------------------------------------
revno: 19798
committer: Abyot Asalefew Gizaw <abyota@xxxxxxxxx>
branch nick: dhis2
timestamp: Wed 2015-08-19 22:13:21 +0200
message:
  tracker-capture: program validation criterias are not applied. user also has the option to proceed with invalid criteria.
added:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/validation-message.html
modified:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-form.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js


--
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
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-form.html	2015-08-09 18:30:54 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/default-form.html	2015-08-19 20:13:21 +0000
@@ -34,9 +34,7 @@
                                         on-select="saveDatavalue(prStDe, innerForm.foo)"
                                         style="width:100%;">
                                 <ui-select-match allow-clear="true" ng-class={{getInputNotifcationClass(prStDe.dataElement.id,false)}} style="width:100%; height:34px; line-height:1.0; padding: 2px 6px; margin-top:5px" placeholder="{{'select_or_search' | translate}}">{{$select.selected.name  || $select.selected}}</ui-select-match>
-                                <ui-select-choices  infinite-scroll="addMoreOptions()"
-                                                    infinite-scroll-distance="2"
-                                                    repeat="option.name as option in optionSets[prStDe.dataElement.optionSet.id].options | filter: $select.search | limitTo:infiniteScroll.currentOptions">
+                                <ui-select-choices  repeat="option.name as option in optionSets[prStDe.dataElement.optionSet.id].options | filter: $select.search | limitTo:30">
                                   <span ng-bind-html="option.name | highlight: $select.search"></span>
                                 </ui-select-choices>
                             </ui-select>
@@ -206,9 +204,7 @@
                                                         on-select="saveDatavalue(prStDes[de.dataElement.id], innerForm.foo)}}"  
                                                         style="width:100%;">
                                                 <ui-select-match allow-clear="true" ng-class={{getInputNotifcationClass(prStDes[de.dataElement.id].dataElement.id,false)}} style="width:100%; height:34px; line-height:1.0; padding: 2px 6px; margin-top:5px" placeholder="{{'select_or_search' | translate}}">{{$select.selected.name  || $select.selected}}</ui-select-match>
-                                                <ui-select-choices  infinite-scroll="addMoreOptions()"
-                                                                    infinite-scroll-distance="2"
-                                                                    repeat="option.name as option in optionSets[prStDes[de.dataElement.id].dataElement.optionSet.id].options | filter: $select.search | limitTo:infiniteScroll.currentOptions">
+                                                <ui-select-choices  repeat="option.name as option in optionSets[prStDes[de.dataElement.id].dataElement.optionSet.id].options | filter: $select.search | limitTo:30">
                                                   <span ng-bind-html="option.name | highlight: $select.search"></span>
                                                 </ui-select-choices>
                                             </ui-select>

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html	2015-07-13 07:37:11 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/default-registration-form.html	2015-08-19 20:13:21 +0000
@@ -12,11 +12,10 @@
                                 ng-required="attribute.mandatory || attribute.unique"
                                 ng-disabled="editingDisabled"
                                 name="foo" 
+                                on-select="validationAndSkipLogic(selectedTei, attribute.id)"
                                 style="width:100%;">
                         <ui-select-match allow-clear="true" style="width:100%; height:34px; line-height:1.0; padding: 2px 6px; margin-top:5px" placeholder="{{'select_or_search' | translate}}">{{$select.selected.name  || $select.selected}}</ui-select-match>
-                        <ui-select-choices  infinite-scroll="addMoreOptions()"
-                                            infinite-scroll-distance="2"
-                                            repeat="option.name as option in optionSets[attributesById[attribute.id].optionSet.id].options | filter: $select.search | limitTo:infiniteScroll.currentOptions">
+                        <ui-select-choices  repeat="option.name as option in optionSets[attributesById[attribute.id].optionSet.id].options | filter: $select.search | limitTo:30">
                           <span ng-bind-html="option.name | highlight: $select.search"></span>
                         </ui-select-choices>
                     </ui-select>
@@ -32,6 +31,7 @@
                                max-date="attribute.allowFutureDate ? '' : 0"
                                ng-model="selectedTei[attribute.id]"
                                ng-disabled="editingDisabled" 
+                               blur-or-change="validationAndSkipLogic(selectedTei, attribute.id)"
                                ng-required="attribute.mandatory || attribute.unique"/>
                     </span>
                     <span ng-switch-when="trueOnly">
@@ -40,6 +40,7 @@
                                class="form-control" 
                                ng-model="selectedTei[attribute.id]" 
                                ng-disabled="editingDisabled" 
+                               ng-change="validationAndSkipLogic(selectedTei, attribute.id)"
                                ng-required="attribute.mandatory || attribute.unique"/>
                     </span>
                     <span ng-switch-when="bool">
@@ -47,6 +48,7 @@
                                 ng-model="selectedTei[attribute.id]" 
                                 class="form-control" 
                                 ng-disabled="editingDisabled" 
+                                ng-change="validationAndSkipLogic(selectedTei, attribute.id)"
                                 ng-required="attribute.mandatory || attribute.unique">
                             <option value="">{{'please_select'| translate}}</option>                        
                             <option value="false">{{'no'| translate}}</option>
@@ -59,6 +61,7 @@
                                class="form-control" 
                                ng-model="selectedTei[attribute.id]" 
                                ng-disabled="editingDisabled" 
+                               ng-blur="validationAndSkipLogic(selectedTei, attribute.id)"
                                ng-required="attribute.mandatory || attribute.unique"/>
                     </span>
                     <span ng-switch-when="email">
@@ -67,6 +70,7 @@
                                class="form-control" 
                                ng-model="selectedTei[attribute.id]" 
                                ng-disabled="editingDisabled" 
+                               ng-blur="validationAndSkipLogic(selectedTei, attribute.id)"
                                ng-required="attribute.mandatory || attribute.unique"/>
                     </span>
                     <span ng-switch-default>
@@ -75,6 +79,7 @@
                                class="form-control" 
                                ng-model="selectedTei[attribute.id]" 
                                ng-disabled="editingDisabled" 
+                               ng-blur="validationAndSkipLogic(selectedTei, attribute.id)"
                                ng-required="attribute.mandatory || attribute.unique"/>                                    
                     </span>
                 </span>                

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js	2015-08-07 10:37:31 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/registration-controller.js	2015-08-19 20:13:21 +0000
@@ -5,12 +5,14 @@
                 $scope,
                 $location,
                 $timeout,
+                $modal,
                 AttributesFactory,
                 DHIS2EventFactory,
                 TEService,
                 CustomFormService,
                 EnrollmentService,
                 DialogService,
+                ModalService,
                 CurrentSelection,
                 OptionSetService,
                 EventUtils,
@@ -25,19 +27,6 @@
     $scope.tei = {};
     $scope.registrationMode = null;
     
-    //Infinite Scroll
-    $scope.infiniteScroll = {};
-    $scope.infiniteScroll.optionsToAdd = 20;
-    $scope.infiniteScroll.currentOptions = 20;
-    
-    $scope.resetInfScroll = function() {
-        $scope.infiniteScroll.currentOptions = $scope.infiniteScroll.optionsToAdd;
-    };
-  
-    $scope.addMoreOptions = function(){
-        $scope.infiniteScroll.currentOptions += $scope.infiniteScroll.optionsToAdd;
-    }; 
-    
     $scope.attributesById = CurrentSelection.getAttributesById();
     if(!$scope.attributesById){
         $scope.attributesById = [];
@@ -97,9 +86,9 @@
         }
     });
         
-    $scope.getAttributes = function(_mode){
+    $scope.getAttributes = function(_mode){        
         var mode = _mode ? _mode : 'ENROLLMENT';
-        AttributesFactory.getByProgram($scope.selectedProgram).then(function(atts){
+        AttributesFactory.getByProgram($scope.selectedProgram).then(function(atts){            
             $scope.attributes = atts;
             $scope.customFormExists = false;
             if($scope.selectedProgram && $scope.selectedProgram.id && $scope.selectedProgram.dataEntryForm && $scope.selectedProgram.dataEntryForm.htmlCode){
@@ -112,35 +101,37 @@
                 $scope.customForm = CustomFormService.getForTrackedEntity($scope.trackedEntityForm, mode);
             }
         });
-    };
+    }; 
     
-    $scope.registerEntity = function(destination){        
+    var goToDashboard = function(destination, teiId){
+        //reset form
+        $scope.selectedTei = {};
+        $scope.selectedEnrollment = {};
+        $scope.outerForm.submitted = false;
 
-        //check for form validity
-        $scope.outerForm.submitted = true;        
-        if( $scope.outerForm.$invalid ){
-            return false;
-        }                   
-        
-        //form is valid, continue the registration
-        //get selected entity        
-        if(!$scope.selectedTei.trackedEntityInstance){
-            $scope.selectedTei.trackedEntity = $scope.tei.trackedEntity = $scope.selectedProgram && $scope.selectedProgram.trackedEntity && $scope.selectedProgram.trackedEntity.id ? $scope.selectedProgram.trackedEntity.id : $scope.trackedEntities.selected.id;
-            $scope.selectedTei.orgUnit = $scope.tei.orgUnit = $scope.selectedOrgUnit.id;
-            $scope.selectedTei.attributes = $scope.selectedTei.attributes = [];
-        }
-        
-        //get tei attributes and their values
-        //but there could be a case where attributes are non-mandatory and
-        //registration form comes empty, in this case enforce at least one value        
-        
-        var result = RegistrationService.processForm($scope.tei, $scope.selectedTei, $scope.attributesById);
-        $scope.formEmpty = result.formEmpty;
-        $scope.tei = result.tei;
-        
-        if($scope.formEmpty){//registration form is empty
-            return false;
-        }
+        if(destination === 'DASHBOARD') {
+            $location.path('/dashboard').search({tei: teiId,                                            
+                                    program: $scope.selectedProgram ? $scope.selectedProgram.id: null});
+        }            
+        else if(destination === 'RELATIONSHIP' ){
+            $scope.tei.trackedEntityInstance = teiId;
+            $scope.broadCastSelections();
+        }
+    };
+    
+    var reloadProfileWidget = function(){
+        var selections = CurrentSelection.get();
+        CurrentSelection.set({tei: $scope.selectedTei, te: $scope.selectedTei.trackedEntity, prs: selections.prs, pr: $scope.selectedProgram, prNames: selections.prNames, prStNames: selections.prStNames, enrollments: selections.enrollments, selectedEnrollment: $scope.selectedEnrollment, optionSets: selections.optionSets});        
+        $timeout(function() { 
+            $rootScope.$broadcast('profileWidget', {});            
+        }, 100);
+    };
+    
+    var notifyRegistrtaionCompletion = function(destination, teiId){
+        goToDashboard( destination ? destination : 'DASHBOARD', teiId );
+    };
+    
+    var performRegistration = function(destination){
         
         RegistrationService.registerOrUpdate($scope.tei, $scope.optionSets, $scope.attributesById).then(function(registrationResponse){
             var reg = registrationResponse.response ? registrationResponse.response : {};            
@@ -199,8 +190,64 @@
                 DialogService.showDialog({}, dialogOptions);
                 return;
             }
-        });        
-    };
+        });
+        
+    };
+    
+    $scope.registerEntity = function(destination){        
+
+        //check for form validity
+        $scope.outerForm.submitted = true;        
+        if( $scope.outerForm.$invalid ){
+            return false;
+        }                   
+        
+        //form is valid, continue the registration
+        //get selected entity        
+        if(!$scope.selectedTei.trackedEntityInstance){
+            $scope.selectedTei.trackedEntity = $scope.tei.trackedEntity = $scope.selectedProgram && $scope.selectedProgram.trackedEntity && $scope.selectedProgram.trackedEntity.id ? $scope.selectedProgram.trackedEntity.id : $scope.trackedEntities.selected.id;
+            $scope.selectedTei.orgUnit = $scope.tei.orgUnit = $scope.selectedOrgUnit.id;
+            $scope.selectedTei.attributes = $scope.selectedTei.attributes = [];
+        }
+        
+        //get tei attributes and their values
+        //but there could be a case where attributes are non-mandatory and
+        //registration form comes empty, in this case enforce at least one value        
+        
+        var result = RegistrationService.processForm($scope.tei, $scope.selectedTei, $scope.attributesById, $scope.selectedProgram);
+        $scope.formEmpty = result.formEmpty;
+        $scope.tei = result.tei;
+        
+        if($scope.formEmpty){//registration form is empty
+            return false;
+        }
+        
+        if(!result.validation.valid){//validation exists           
+            var modalInstance = $modal.open({
+                templateUrl: 'components/registration/validation-message.html',
+                controller: 'ValidationMessageController',
+                resolve: {
+                    validation: function () {
+                        return result.validation;
+                    }
+                }
+            });
+
+            modalInstance.result.then(function (res) {
+                if(!res) {//strict validation
+                    return false;
+                }
+                else{//not-strict validation
+                    performRegistration(destination);
+                }
+            }, function () {
+            });        
+        }
+        else{//no validation
+            performRegistration(destination);
+        }        
+    };
+    
     
     $scope.broadCastSelections = function(){
         angular.forEach($scope.tei.attributes, function(att){
@@ -215,6 +262,9 @@
         }, 100);
     };
     
+    /*$scope.validationAndSkipLogic = function(tei, field){
+    };*/
+    
     $scope.interacted = function(field) {
         var status = false;
         if(field){            
@@ -222,32 +272,20 @@
         }
         return status;        
     };
-    
-    var goToDashboard = function(destination, teiId){
-        //reset form
-        $scope.selectedTei = {};
-        $scope.selectedEnrollment = {};
-        $scope.outerForm.submitted = false;
-
-        if(destination === 'DASHBOARD') {
-            $location.path('/dashboard').search({tei: teiId,                                            
-                                    program: $scope.selectedProgram ? $scope.selectedProgram.id: null});
-        }            
-        else if(destination === 'RELATIONSHIP' ){
-            $scope.tei.trackedEntityInstance = teiId;
-            $scope.broadCastSelections();
-        }
-    };
-    
-    var reloadProfileWidget = function(){
-        var selections = CurrentSelection.get();
-        CurrentSelection.set({tei: $scope.selectedTei, te: $scope.selectedTei.trackedEntity, prs: selections.prs, pr: $scope.selectedProgram, prNames: selections.prNames, prStNames: selections.prStNames, enrollments: selections.enrollments, selectedEnrollment: $scope.selectedEnrollment, optionSets: selections.optionSets});        
-        $timeout(function() { 
-            $rootScope.$broadcast('profileWidget', {});            
-        }, 100);
-    };
-    
-    var notifyRegistrtaionCompletion = function(destination, teiId){
-        goToDashboard( destination ? destination : 'DASHBOARD', teiId );
+})
+
+.controller('ValidationMessageController',
+        function ($scope,
+                $modalInstance,                
+                validation) {
+                    
+    $scope.validationResult = validation;
+
+    $scope.proceed = function () {
+        $modalInstance.close(true);
+    };
+
+    $scope.cancel = function () {
+        $modalInstance.close(false);
     };
 });
\ No newline at end of file

=== added file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/validation-message.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/validation-message.html	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/registration/validation-message.html	2015-08-19 20:13:21 +0000
@@ -0,0 +1,18 @@
+<div class="modal-header page">
+    <h3>{{'validation_result' | translate}}</h3>
+</div>
+<div class="modal-body page">
+    <table class="table table-striped table-bordered">  
+        <tr ng-repeat="message in validationResult.messages">            
+            <td>
+                {{message.name}} {{'is_expected' | translate}} <strong>{{message.expected}}</strong> {{'but_found' | translate}} <strong>{{message.found}}</strong>
+            </td>
+        </tr>                                    
+    </table>
+    
+    <div class="alert alert-warning">{{'do_you_want_to_proceed' | translate}}</div>
+</div>
+<div class="modal-footer page">        
+    <button class="btn btn-primary" data-ng-click="proceed()">{{'yes'| translate}}</button>
+    <button class="btn btn-default" data-ng-click="cancel()">{{'no'| translate}}</button>
+</div>
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties	2015-08-09 22:28:42 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties	2015-08-19 20:13:21 +0000
@@ -21,6 +21,7 @@
 not_yet_enrolled_report=Not yet enrolled. Reporting not possible
 no_data_report=No record exists for reporting
 empty_notes=Empty notes list.
+empty=Empty
 event=Event
 no_event_is_yet_created=No event is available for data entry. Please create one.
 event_creation=Please create one from below
@@ -96,6 +97,7 @@
 complete=Complete
 incomplete=Incomplete
 validate=Validate
+validation_result=Validation Result
 status=Status
 details=Details
 _details=details
@@ -291,6 +293,7 @@
 are_you_sure_to_incomplete_event=Are you sure you want to incomplete the selected event?
 are_you_sure_to_skip_event=Are you sure you want to skip the selected event?
 are_you_sure_to_unskip_event=Are you sure you want to schedule back the selected event?
+do_you_want_to_proceed=Do you want to proceed with invalid criteria?
 more=More
 under_construction=Under construction.    
 advanced_search=Advanced search
@@ -341,4 +344,6 @@
 nov=November
 dec=December
 week=Week
-save_layout_as_default=Save dashboard layout as default
\ No newline at end of file
+save_layout_as_default=Save dashboard layout as default
+is_expected=is expected
+but_found=but found
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js	2015-08-07 16:01:27 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js	2015-08-19 20:13:21 +0000
@@ -514,7 +514,7 @@
 })
 
 /* service to deal with TEI registration and update */
-.service('RegistrationService', function(TEIService, $q){
+.service('RegistrationService', function(TEIService, $q, $translate){
     return {
         registerOrUpdate: function(tei, optionSets, attributesById){
             if(tei){
@@ -532,19 +532,42 @@
                 return def.promise;
             }            
         },
-        processForm: function(existingTei, formTei, attributesById){
+        processForm: function(existingTei, formTei, attributesById, program){
             var tei = angular.copy(existingTei);
+            var enrollmentValidation = {valid: true, messages: [], attributes: []};
+            if(program && program.validationCriterias){
+                for(var key in program.validationCriterias){
+                    angular.forEach(program.validationCriterias[key], function(vc){
+                        if(vc.property && vc.value){                                    
+                            if(vc.operator === 0){
+                                enrollmentValidation.valid = vc.value === formTei[key];
+                            }
+                            else if(vc.operator === 1){                                
+                                enrollmentValidation.valid = vc.value > formTei[key];
+                            }
+                            else{
+                                enrollmentValidation.valid = vc.value < formTei[key];
+                            }
+
+                            if(!enrollmentValidation.valid){
+                                enrollmentValidation.messages.push({name: attributesById[key].name, expected: vc.value, found: formTei[key] ? formTei[key] : $translate.instant('empty')});                                    
+                            }                                
+                        }
+                    });                    
+                }
+            }            
+            
             tei.attributes = [];
-            var formEmpty = true;
+            var formEmpty = true;            
             for(var k in attributesById){
                 if( formTei[k] ){
                     var att = attributesById[k];
                     tei.attributes.push({attribute: att.id, value: formTei[k], displayName: att.name, type: att.valueType});
-                    formEmpty = false;
+                    formEmpty = false;              
                 }
                 delete tei[k];
             }
-            return {tei: tei, formEmpty: formEmpty};
+            return {tei: tei, formEmpty: formEmpty, validation: enrollmentValidation};
         }
     };
 })

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js	2015-08-07 16:01:27 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js	2015-08-19 20:13:21 +0000
@@ -275,7 +275,7 @@
         return $.ajax( {
             url: '../api/programs/' + id + '.json',
             type: 'GET',
-            data: 'fields=id,name,type,version,dataEntryMethod,dateOfEnrollmentDescription,dateOfIncidentDescription,displayIncidentDate,ignoreOverdueEvents,selectEnrollmentDatesInFuture,selectIncidentDatesInFuture,onlyEnrollOnce,externalAccess,displayOnAllOrgunit,registration,relationshipText,relationshipFromA,relatedProgram[id,name],relationshipType[id,name],trackedEntity[id,name,description],userRoles[id,name],organisationUnits[id,name],userRoles[id,name],programStages[id,name,version,minDaysFromStart,standardInterval,periodType,generatedByEnrollmentDate,reportDateDescription,repeatable,autoGenerateEvent,openAfterEnrollment,reportDateToUse],dataEntryForm[name,style,htmlCode,format],programTrackedEntityAttributes[displayInList,mandatory,allowFutureDate,trackedEntityAttribute[id,unique]]'
+            data: 'fields=id,name,type,version,dataEntryMethod,dateOfEnrollmentDescription,dateOfIncidentDescription,displayIncidentDate,ignoreOverdueEvents,selectEnrollmentDatesInFuture,selectIncidentDatesInFuture,onlyEnrollOnce,externalAccess,displayOnAllOrgunit,registration,relationshipText,relationshipFromA,relatedProgram[id,name],relationshipType[id,name],trackedEntity[id,name,description],userRoles[id,name],organisationUnits[id,name],userRoles[id,name],programStages[id,name,version,minDaysFromStart,standardInterval,periodType,generatedByEnrollmentDate,reportDateDescription,repeatable,autoGenerateEvent,openAfterEnrollment,reportDateToUse],dataEntryForm[name,style,htmlCode,format],programTrackedEntityAttributes[displayInList,mandatory,allowFutureDate,trackedEntityAttribute[id,unique]],validationCriterias[id,name,operator,value,property]'
         }).done( function( program ){            
             var ou = {};
             if(program.organisationUnits){
@@ -292,7 +292,21 @@
                 });
             }
             program.userRoles = ur;
-
+            
+            var vc = {};
+            if(program.validationCriterias){
+                _.each(_.values( program.validationCriterias), function(c){
+                    if(vc[c.property]){
+                        vc[c.property].push(c);
+                    }
+                    else{
+                        vc[c.property] = [];
+                        vc[c.property].push(c);
+                    }
+                });
+            }            
+            program.validationCriterias = vc;
+            
             dhis2.tc.store.set( 'programs', program );  
         });
     };

=== modified file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js	2015-08-18 12:01:26 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js	2015-08-19 20:13:21 +0000
@@ -401,8 +401,8 @@
 
                             //check if attribute has optionset
                             if (att.optionSetValue) {
-                                var optionSetId = att.optionSet.id;
-                                newInputField = '<ui-select theme="select2" ' + commonInputFieldProperty + ' >' +
+                                var optionSetId = att.optionSet.id;                                
+                                newInputField = '<ui-select theme="select2" ' + commonInputFieldProperty + '  on-select="validationAndSkipLogic(selectedTei,\'' + attId + '\')" >' +
                                         '<ui-select-match style="width:100%;" allow-clear="true" placeholder="' + $translate.instant('select_or_search') + '">{{$select.selected.name || $select.selected}}</ui-select-match>' +
                                         '<ui-select-choices ' +
                                         'repeat="option.name as option in optionSets.' + optionSetId + '.options | filter: $select.search | limitTo:30">' +