← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 19527: Refactor for quicker rules execution

 

------------------------------------------------------------
revno: 19527
committer: Markus Bekken <markus.bekken@xxxxxxxxx>
branch nick: dhis2
timestamp: Wed 2015-07-01 08:24:47 +0200
message:
  Refactor for quicker rules execution
modified:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/controllers.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.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-event-capture/scripts/controllers.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/controllers.js	2015-06-22 08:45:36 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/controllers.js	2015-07-01 06:24:47 +0000
@@ -30,7 +30,8 @@
                 ModalService,
                 DialogService,
                 AuthorityService,
-                TrackerRulesExecutionService) {
+                TrackerRulesExecutionService,
+                TrackerRulesFactory) {
     //selected org unit
     $scope.selectedOrgUnit = '';
     $scope.treeLoaded = false;    
@@ -106,6 +107,7 @@
         $scope.selectedProgramStage = null;
         $scope.programValidations = [];
         $scope.programIndicators = [];
+        $scope.allProgramRules = [];
         $scope.dhis2Events = [];
         $scope.currentEvent = {};
         $scope.currentEventOriginialValue = {};
@@ -193,12 +195,12 @@
                 }
                 $scope.newDhis2Event.eventDate = '';
 
-                MetaDataFactory.getByProgram('programValidations', $scope.selectedProgram.id).then(function(pvs){
-                    $scope.programValidations = pvs;
-                    MetaDataFactory.getByProgram('programIndicators', $scope.selectedProgram.id).then(function(pis){
-                        $scope.programIndicators = pis;
+                MetaDataFactory.getByProgram('programIndicators', $scope.selectedProgram.id).then(function(pis){
+                    $scope.programIndicators = pis;
+                    TrackerRulesFactory.getRules($scope.selectedProgram.id).then(function(rules){                    
+                        $scope.allProgramRules = rules;
                         $scope.loadEvents();
-                    });                    
+                    }); 
                 });
             });
         }
@@ -304,7 +306,9 @@
                     if(!$scope.sortHeader.id){
                         $scope.sortEventGrid({name: $scope.selectedProgramStage.reportDateDescription ? $scope.selectedProgramStage.reportDateDescription : 'incident_date', id: 'event_date', type: 'date', compulsory: false, showFilter: false, show: true});
                     }
-                }                
+                }     
+                
+                
                 $scope.eventFetched = true;
             });
         }
@@ -952,8 +956,14 @@
         $scope.currentEvent.event = !$scope.currentEvent.event ? 'SINGLE_EVENT' : $scope.currentEvent.event;
         $scope.eventsByStage = [];
         $scope.eventsByStage[$scope.selectedProgramStage.id] = [$scope.currentEvent];
-        TrackerRulesExecutionService.executeRules($scope.selectedProgram.id,$scope.currentEvent,$scope.eventsByStage,$scope.prStDes,null,false);
+        var evs = {all: [$scope.currentEvent], byStage: $scope.eventsByStage};
+        
+        var flag = {debug: true, verbose: true};
+        
+        //TrackerRulesExecutionService.executeRules($scope.selectedProgram.id,$scope.currentEvent,$scope.eventsByStage,$scope.prStDes,null,false);
+        TrackerRulesExecutionService.executeRules($scope.allProgramRules, $scope.currentEvent, evs, $scope.prStDes, $scope.selectedTei, $scope.selectedEnrollment, flag);
     };
+       
     
     $scope.formatNumberResult = function(val){        
         return dhis2.validation.isNumber(val) ? val : '';

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js	2015-06-22 07:04:03 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/services.js	2015-07-01 06:24:47 +0000
@@ -319,23 +319,9 @@
     };    
 })
 
-    /* Returns a function for getting rules for a specific program */
-.factory('TrackerRulesFactory', function($q,$rootScope,ECStorageService){
-    return{
-        getOldProgramStageRules :function(programUid, programstageUid) {
-            var rules = this.getProgramRules(programUid);
-            
-            //Only keep the rules actually matching the program stage we are in, or rules with no program stage defined.
-            var programStageRules = [];
-            angular.forEach(rules, function(rule) {
-                if(rule.programstage_uid == null || rule.programstage_uid == "" || rule.programstage_uid == programstageUid) {
-                   programStageRules.push(rule);
-                }
-            });
-            
-            return programStageRules;
-        },
-        
+/* Returns a function for getting rules for a specific program */
+.factory('TrackerRulesFactory', function($q,$rootScope,ECStorageService, MetaDataFactory){
+    return{        
         getProgramStageRules : function(programUid, programStageUid){
             var def = $q.defer();
             
@@ -345,8 +331,9 @@
                     var programRulesArray = [];
                     //Loop through and add the rules belonging to this program and program stage
                     angular.forEach(rules, function(rule){
-                       if(rule.program.id == programUid) {
-                           if(!rule.programStage || !rule.programStage.id || rule.programStage.id == programStageUid) {
+                       if(rule.program.id === programUid) {
+                           if(!rule.programStage || !rule.programStage.id || rule.programStage.id === programStageUid) {
+                                rule.actions = [];
                                 programRulesArray.push(rule);
                             }
                        }
@@ -359,6 +346,92 @@
             });
                         
             return def.promise;
+        },        
+        getRules : function(programUid){            
+            var def = $q.defer();            
+            MetaDataFactory.getAll('constants').then(function(constants) {
+                MetaDataFactory.getByProgram('programIndicators',programUid).then(function(pis){                    
+                    var variables = [];
+                    var programRules = [];
+                    angular.forEach(pis, function(pi){                    
+                        var newAction = {
+                                id:pi.id,
+                                content:pi.displayDescription ? pi.displayDescription : pi.name,
+                                data:pi.expression,
+                                programRuleActionType:'DISPLAYKEYVALUEPAIR',
+                                location:'indicators'
+                            };
+                        var newRule = {
+                                name:pi.name,
+                                id: pi.id,
+                                shortname:pi.shortname,
+                                code:pi.code,
+                                program:pi.program,
+                                description:pi.description,
+                                condition:pi.filter ? pi.filter : 'true',
+                                programRuleActions: [newAction]
+                            };
+
+                        programRules.push(newRule);
+
+                        var variablesInCondition = newRule.condition.match(/#{\w+.?\w*}/g);
+                        var variablesInData = newAction.data.match(/#{\w+.?\w*}/g);
+
+                        var pushDirectAddressedVariable = function(variableWithCurls) {
+                            var variableName = variableWithCurls.replace("#{","").replace("}","");
+                            var variableNameParts = variableName.split('.');
+
+
+                            if(variableNameParts.length === 2) {
+                                //this is a programstage and dataelement specification. translate to program variable:
+                                variables.push({
+                                    name:variableName,
+                                    programRuleVariableSourceType:'DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE',
+                                    dataElement:variableNameParts[1],
+                                    programStage:variableNameParts[0],
+                                    program:programUid
+                                });
+                            }
+                            else if(variableNameParts.length === 1)
+                            {
+                                //This is an attribute - let us translate to program variable:
+                                variables.push({
+                                    name:variableName,
+                                    programRuleVariableSourceType:'TEI_ATTRIBUTE',
+                                    trackedEntityAttribute:variableNameParts[0],
+                                    program:programUid
+                                });
+                            }
+
+                        };
+
+                        angular.forEach(variablesInCondition, function(variableInCondition) {
+                            pushDirectAddressedVariable(variableInCondition);
+                        });
+
+                        angular.forEach(variablesInData, function(variableInData) {
+                            pushDirectAddressedVariable(variableInData);
+                        });
+                    });
+
+                    var programIndicators = {rules:programRules, variables:variables};
+                    
+                    MetaDataFactory.getByProgram('programValidations',programUid).then(function(programValidations){                    
+                        MetaDataFactory.getByProgram('programRuleVariables',programUid).then(function(programVariables){                    
+                            MetaDataFactory.getByProgram('programRules',programUid).then(function(prs){
+                                var programRules = [];
+                                angular.forEach(prs, function(rule){
+                                    rule.actions = [];
+                                    rule.programStageId = rule.programStage && rule.programStage.id ? rule.programStage.id : null;
+                                    programRules.push(rule);
+                                });                                
+                                def.resolve({constants: constants, programIndicators: programIndicators, programValidations: programValidations, programVariables: programVariables, programRules: programRules});
+                            });
+                        });
+                    });
+                }); 
+            });                        
+            return def.promise;
         }
     };  
 })

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js	2015-06-23 13:17:50 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js	2015-07-01 06:24:47 +0000
@@ -1,10 +1,10 @@
 /* global angular, trackerCapture */
 
 trackerCapture.controller('DataEntryController',
-        function($rootScope,
+        function ($rootScope,
                 $scope,
-				$modal,
-				$filter,
+                $modal,
+                $filter,
                 $log,
                 $timeout,
                 Paginator,
@@ -18,10 +18,11 @@
                 OptionSetService,
                 ModalService,
                 CurrentSelection,
-				TrackerRulesExecutionService,
-				CustomFormService,
-				PeriodService) {
-    
+                TrackerRulesExecutionService,
+                CustomFormService,
+                PeriodService,
+                TrackerRulesFactory) {
+
     //Data entry form
     $scope.outerForm = {};
     $scope.displayCustomForm = false;
@@ -34,49 +35,49 @@
     //variable is set while looping through the program stages later.
     $scope.stagesCanBeShownAsTable = false;
     $scope.showHelpText = {};
-	$scope.hiddenFields = {};
-    
+    $scope.hiddenFields = {};
+
     var userProfile = SessionStorageService.get('USER_PROFILE');
     var storedBy = userProfile && userProfile.username ? userProfile.username : '';
-    
+
     var today = DateUtils.getToday();
     $scope.invalidDate = false;
-    
+
     //note
     $scope.note = '';
-    
+
     //event color legend
     $scope.eventColors = [
-                            {color: 'alert-success', description: 'completed'},
-                            {color: 'alert-info', description: 'executed'},
-                            {color: 'alert-warning', description: 'ontime'},
-                            {color: 'alert-danger', description: 'overdue'},
-                            {color: 'alert-default', description: 'skipped'}
-                         ];
+        {color: 'alert-success ', description: 'completed'},
+        {color: 'alert-info ', description: 'executed'},
+        {color: 'alert-warning ', description: 'ontime'},
+        {color: 'alert-danger ', description: 'overdue'},
+        {color: 'alert-default ', description: 'skipped'}
+    ];
     $scope.showEventColors = false;
-    
+
     //listen for rule effect changes
-    $scope.$on('ruleeffectsupdated', function(event, args) {
-        if($rootScope.ruleeffects[args.event]) {
+    $scope.$on('ruleeffectsupdated', function (event, args) {
+        if ($rootScope.ruleeffects[args.event]) {
             //Establish which event was affected:
             var affectedEvent = $scope.currentEvent;
             //In most cases the updated effects apply to the current event. In case the affected event is not the current event, fetch the correct event to affect:
-            if(args.event !== affectedEvent.event) {
-                angular.forEach($scope.currentStageEvents, function(searchedEvent) {
-                    if(searchedEvent.event === args.event) {
+            if (args.event !== affectedEvent.event) {
+                angular.forEach($scope.currentStageEvents, function (searchedEvent) {
+                    if (searchedEvent.event === args.event) {
                         affectedEvent = searchedEvent;
                     }
                 });
             }
-            
-            angular.forEach($rootScope.ruleeffects[args.event], function(effect) {
-                if( effect.dataElement ) {
+
+            angular.forEach($rootScope.ruleeffects[args.event], function (effect) {
+                if (effect.dataElement) {
                     //in the data entry controller we only care about the "hidefield" actions
-                    if(effect.action === "HIDEFIELD") {
-                        if(effect.dataElement) {
-                            if(effect.ineffect && affectedEvent[effect.dataElement.id]) {
+                    if (effect.action === "HIDEFIELD") {
+                        if (effect.dataElement) {
+                            if (effect.ineffect && affectedEvent[effect.dataElement.id]) {
                                 //If a field is going to be hidden, but contains a value, we need to take action;
-                                if(effect.content) {
+                                if (effect.content) {
                                     //TODO: Alerts is going to be replaced with a proper display mecanism.
                                     alert(effect.content);
                                 }
@@ -87,7 +88,7 @@
 
                                 //Blank out the value:
                                 affectedEvent[effect.dataElement.id] = "";
-                                $scope.saveDatavalueForEvent($scope.prStDes[effect.dataElement.id],null,affectedEvent);
+                                $scope.saveDatavalueForEvent($scope.prStDes[effect.dataElement.id], null, affectedEvent);
                             }
 
                             $scope.hiddenFields[effect.dataElement.id] = effect.ineffect;
@@ -100,196 +101,204 @@
             });
         }
     });
-    
+
     //check if field is hidden
-    $scope.isHidden = function(id) {
+    $scope.isHidden = function (id) {
         //In case the field contains a value, we cant hide it. 
         //If we hid a field with a value, it would falsely seem the user was aware that the value was entered in the UI.
-        if($scope.currentEvent[id]) {
-           return false; 
+        if ($scope.currentEvent[id]) {
+            return false;
         }
         else {
             return $scope.hiddenFields[id];
         }
-    }; 
-    
-    $scope.executeRules = function() {
+    };
+
+    $scope.executeRules = function () {        
+        var evs = {all: $scope.allEventsSorted, byStage: $scope.eventsByStageAsc};
+        var flag = {debug: true, verbose: true};
         //If the events is displayed in a table, it is necessary to run the rules for all visible events.
-        if($scope.currentStage.displayEventsInTable) {
-           angular.forEach($scope.currentStageEvents, function(event) {
-               TrackerRulesExecutionService.executeRules($scope.selectedProgramId,event,$scope.eventsByStage,$scope.prStDes,$scope.selectedTei,$scope.selectedEnrollment,false);
-           });
+        if ($scope.currentStage.displayEventsInTable) {
+            angular.forEach($scope.currentStageEvents, function (event) {
+                TrackerRulesExecutionService.executeRules($scope.allProgramRules, event, evs, $scope.prStDes, $scope.selectedTei, $scope.selectedEnrollment, flag);
+            });
         } else {
-           TrackerRulesExecutionService.executeRules($scope.selectedProgramId,$scope.currentEvent,$scope.eventsByStage,$scope.prStDes,$scope.selectedTei,$scope.selectedEnrollment,false);
+            TrackerRulesExecutionService.executeRules($scope.allProgramRules, $scope.currentEvent, evs, $scope.prStDes, $scope.selectedTei, $scope.selectedEnrollment, flag);
         }
     };
-    
-    
+
+
     //listen for the selected items
-    $scope.$on('dashboardWidgets', function() {
+    $scope.$on('dashboardWidgets', function () {
         $scope.showDataEntryDiv = false;
         $scope.showEventCreationDiv = false;
         $scope.currentEvent = null;
         $scope.currentStage = null;
         $scope.currentStageEvents = null;
         $scope.totalEvents = 0;
-            
+
         $scope.allowEventCreation = false;
-        $scope.repeatableStages = [];        
+        $scope.repeatableStages = [];
         $scope.eventsByStage = [];
+        $scope.eventsByStageAsc = [];
         $scope.programStages = [];
         $rootScope.ruleeffects = {};
-		$scope.prStDes = [];
-        
-        var selections = CurrentSelection.get();          
+        $scope.prStDes = [];
+        $scope.allProgramRules = [];
+
+        var selections = CurrentSelection.get();
         $scope.selectedOrgUnit = SessionStorageService.get('SELECTED_OU');
-        $scope.selectedEntity = selections.tei;      
-        $scope.selectedProgram = selections.pr;        
-        $scope.selectedEnrollment = selections.selectedEnrollment;   
+        $scope.selectedEntity = selections.tei;
+        $scope.selectedProgram = selections.pr;
+        $scope.selectedEnrollment = selections.selectedEnrollment;
         $scope.optionSets = selections.optionSets;
-        
-        $scope.stagesById = [];        
-        if($scope.selectedOrgUnit && $scope.selectedProgram && $scope.selectedProgram.id && $scope.selectedEntity && $scope.selectedEnrollment && $scope.selectedEnrollment.enrollment){            
-            ProgramStageFactory.getByProgram($scope.selectedProgram).then(function(stages){
+
+        $scope.stagesById = [];
+        if ($scope.selectedOrgUnit && $scope.selectedProgram && $scope.selectedProgram.id && $scope.selectedEntity && $scope.selectedEnrollment && $scope.selectedEnrollment.enrollment) {
+            ProgramStageFactory.getByProgram($scope.selectedProgram).then(function (stages) {
                 $scope.programStages = stages;
-                angular.forEach(stages, function(stage){
-                    if(stage.openAfterEnrollment){
+                angular.forEach(stages, function (stage) {
+                    if (stage.openAfterEnrollment) {
                         $scope.currentStage = stage;
-                    }                   
-                    
-                    angular.forEach(stage.programStageDataElements, function(prStDe){
+                    }
+
+                    angular.forEach(stage.programStageDataElements, function (prStDe) {
                         $scope.prStDes[prStDe.dataElement.id] = prStDe;
                     });
-                    
+
                     $scope.stagesById[stage.id] = stage;
                     $scope.eventsByStage[stage.id] = [];
-                    
+
                     //If one of the stages has less than 7 data elements, allow sorting as table:
-                    if(stage.programStageDataElements.length < 7) {
+                    if (stage.programStageDataElements.length < 7) {
                         $scope.stagesCanBeShownAsTable = true;
                     }
                 });
-                
+
                 $scope.programStages = orderByFilter($scope.programStages, '-sortOrder').reverse();
-                if(!$scope.currentStage){
+                if (!$scope.currentStage) {
                     $scope.currentStage = $scope.programStages[0];
                 }
-                $scope.getEvents();                
+                
+                TrackerRulesFactory.getRules($scope.selectedProgram.id).then(function(rules){                    
+                    $scope.allProgramRules = rules;
+                    $scope.getEvents();
+                });           
             });
         }
     });
-    
-    $scope.getEvents = function(){
-        
+
+    $scope.getEvents = function () {
+
         var events = CurrentSelection.getSelectedTeiEvents();
         events = $filter('filter')(events, {program: $scope.selectedProgram.id});
-        
-        if(angular.isObject(events)){
-            angular.forEach(events, function(dhis2Event){                    
-                if(dhis2Event.enrollment === $scope.selectedEnrollment.enrollment && dhis2Event.orgUnit){
-                    if(dhis2Event.notes){
+
+        if (angular.isObject(events)) {
+            angular.forEach(events, function (dhis2Event) {
+                if (dhis2Event.enrollment === $scope.selectedEnrollment.enrollment && dhis2Event.orgUnit) {
+                    if (dhis2Event.notes) {
                         dhis2Event.notes = orderByFilter(dhis2Event.notes, '-storedDate');
-                        angular.forEach(dhis2Event.notes, function(note){
+                        angular.forEach(dhis2Event.notes, function (note) {
                             note.storedDate = DateUtils.formatToHrsMins(note.storedDate);
                         });
                     }
 
                     var eventStage = $scope.stagesById[dhis2Event.programStage];
-                    if(angular.isObject(eventStage)){
-                        dhis2Event.name = eventStage.name; 
+                    if (angular.isObject(eventStage)) {
+                        dhis2Event.name = eventStage.name;
                         dhis2Event.reportDateDescription = eventStage.reportDateDescription;
                         dhis2Event.dueDate = DateUtils.formatFromApiToUser(dhis2Event.dueDate);
                         dhis2Event.sortingDate = dhis2Event.dueDate;
 
-                        if(dhis2Event.eventDate){
+                        if (dhis2Event.eventDate) {
                             dhis2Event.eventDate = DateUtils.formatFromApiToUser(dhis2Event.eventDate);
                             dhis2Event.sortingDate = dhis2Event.eventDate;
                             dhis2Event.editingNotAllowed = setEventEditing(dhis2Event, eventStage);
-                        }                       
+                        }
 
                         dhis2Event.statusColor = EventUtils.getEventStatusColor(dhis2Event);
                         dhis2Event = EventUtils.processEvent(dhis2Event, eventStage, $scope.optionSets, $scope.prStDes);
                         $scope.eventsByStage[dhis2Event.programStage].push(dhis2Event);
 
-                        if($scope.currentStage && $scope.currentStage.id === dhis2Event.programStage){
-                            $scope.currentEvent = dhis2Event; 
+                        if ($scope.currentStage && $scope.currentStage.id === dhis2Event.programStage) {
+                            $scope.currentEvent = dhis2Event;
                         }
                     }
                 }
             });
-            
+
             sortEventsByStage(null);
-            $scope.showDataEntry($scope.currentEvent, true);            
+            $scope.showDataEntry($scope.currentEvent, true);
         }
     };
-    
-    var setEventEditing = function(dhis2Event, stage){
+
+    var setEventEditing = function (dhis2Event, stage) {
         return dhis2Event.editingNotAllowed = dhis2Event.orgUnit !== $scope.selectedOrgUnit.id || (stage.blockEntryForm && dhis2Event.status === 'COMPLETED');
     };
-    
-    $scope.enableRescheduling = function(){
+
+    $scope.enableRescheduling = function () {
         $scope.schedulingEnabled = !$scope.schedulingEnabled;
     };
-    
-    $scope.stageCanBeShownAsTable = function(stage) {
-        if(stage.programStageDataElements && stage.programStageDataElements.length < 7) {
+
+    $scope.stageCanBeShownAsTable = function (stage) {
+        if (stage.programStageDataElements && stage.programStageDataElements.length < 7) {
             return true;
         }
         return false;
     };
-    
-    $scope.toggleEventsTableDisplay = function() {
+
+    $scope.toggleEventsTableDisplay = function () {
         $scope.showEventsAsTables = !$scope.showEventsAsTables;
-        angular.forEach($scope.programStages, function(stage){
-            if(stage.programStageDataElements.length < 7) {
+        angular.forEach($scope.programStages, function (stage) {
+            if (stage.programStageDataElements.length < 7) {
                 stage.displayEventsInTable = $scope.showEventsAsTables;
-                if($scope.currentStage === stage) {
+                if ($scope.currentStage === stage) {
                     $scope.getDataEntryForm();
                 }
             }
         });
     };
-    
-    $scope.stageNeedsEvent = function(stage){
-        
+
+    $scope.stageNeedsEvent = function (stage) {
+
         //In case the event is a table, we sould always allow adding more events(rows)
-        if(stage.displayEventsInTable) {
-            return true;
-        }
-        
-        if($scope.eventsByStage[stage.id].length < 1){
-            return true;
-        }
-
-        if(stage.repeatable){
-            for(var j=0; j<$scope.eventsByStage[stage.id].length; j++){
-                if(!$scope.eventsByStage[stage.id][j].eventDate && $scope.eventsByStage[stage.id][j].status !== 'SKIPPED'){
+        if (stage.displayEventsInTable) {
+            return true;
+        }
+
+        if ($scope.eventsByStage[stage.id].length < 1) {
+            return true;
+        }
+
+        if (stage.repeatable) {
+            for (var j = 0; j < $scope.eventsByStage[stage.id].length; j++) {
+                if (!$scope.eventsByStage[stage.id][j].eventDate && $scope.eventsByStage[stage.id][j].status !== 'SKIPPED') {
                     return false;
                 }
             }
-            return true;            
-        }        
-        return false;        
+            return true;
+        }
+        return false;
     };
-    
-    $scope.showCreateEvent = function(stage){
-        
+
+    $scope.showCreateEvent = function (stage) {
+
         var dummyEvent = EventUtils.createDummyEvent($scope.eventsByStage[stage.id], $scope.selectedEntity, $scope.selectedProgram, stage, $scope.selectedOrgUnit, $scope.selectedEnrollment);
-        
+
         var modalInstance = $modal.open({
             templateUrl: 'components/dataentry/new-event.html',
             controller: 'EventCreationController',
             resolve: {
-                stagesById: function(){
+                stagesById: function () {
                     return $scope.stagesById;
                 },
-                dummyEvent: function(){
+                dummyEvent: function () {
                     return dummyEvent;
                 },
-                eventPeriods: function(){
+                eventPeriods: function () {
                     return $scope.eventPeriods;
                 },
-                autoCreate: function() {
+                autoCreate: function () {
                     //In case the programstage is a table, autocreate
                     return stage.displayEventsInTable;
                 }
@@ -297,98 +306,109 @@
         });
 
         modalInstance.result.then(function (ev) {
-            if(angular.isObject(ev)){
+            if (angular.isObject(ev)) {
                 var newEvent = ev;
                 newEvent.orgUnitName = dummyEvent.orgUnitName;
                 newEvent.name = dummyEvent.name;
                 newEvent.reportDateDescription = dummyEvent.reportDateDescription;
                 newEvent.sortingDate = ev.eventDate ? ev.eventDate : ev.dueDate,
-                newEvent.statusColor = EventUtils.getEventStatusColor(ev);
+                        newEvent.statusColor = EventUtils.getEventStatusColor(ev);
                 newEvent.eventDate = DateUtils.formatFromApiToUser(ev.eventDate);
-                newEvent.dueDate =  DateUtils.formatFromApiToUser(ev.dueDate);
+                newEvent.dueDate = DateUtils.formatFromApiToUser(ev.dueDate);
                 newEvent.enrollmentStatus = dummyEvent.enrollmentStatus;
-                
-                if(dummyEvent.coordinate){
+
+                if (dummyEvent.coordinate) {
                     newEvent.coordinate = {};
                 }
-                
+
                 //Have to make sure the event is preprocessed - this does not happen unless "Dashboardwidgets" is invoked.
                 newEvent = EventUtils.processEvent(newEvent, stage, $scope.optionSets, $scope.prStDes);
-                
-                
+
+
                 $scope.eventsByStage[newEvent.programStage].push(newEvent);
                 $scope.currentEvent = newEvent;
                 sortEventsByStage('ADD');
 
                 $scope.currentEvent = null;
                 $scope.showDataEntry(newEvent, false);
-            }            
+            }
         }, function () {
         });
-    };    
-       
-    $scope.showDataEntry = function(event, rightAfterEnrollment){        
-        if(event){
-            
-            Paginator.setItemCount( $scope.eventsByStage[event.programStage].length );
-            Paginator.setPage( $scope.eventsByStage[event.programStage].indexOf( event ) );
-            Paginator.setPageCount( Paginator.getItemCount() );
-            Paginator.setPageSize( 1 );
-            Paginator.setToolBarDisplay( 5 );
-        
-            if($scope.currentEvent && !rightAfterEnrollment && $scope.currentEvent.event === event.event){
+    };
+
+    $scope.showDataEntry = function (event, rightAfterEnrollment) {
+        if (event) {
+
+            Paginator.setItemCount($scope.eventsByStage[event.programStage].length);
+            Paginator.setPage($scope.eventsByStage[event.programStage].indexOf(event));
+            Paginator.setPageCount(Paginator.getItemCount());
+            Paginator.setPageSize(1);
+            Paginator.setToolBarDisplay(5);
+
+            if ($scope.currentEvent && !rightAfterEnrollment && $scope.currentEvent.event === event.event) {
                 //clicked on the same stage, do toggling
                 $scope.currentEvent = null;
                 $scope.currentElement = {id: '', saved: false};
-                $scope.showDataEntryDiv = !$scope.showDataEntryDiv;      
+                $scope.showDataEntryDiv = !$scope.showDataEntryDiv;
             }
-            else{
-                $scope.currentElement = {};
+            else {
+                $scope.currentElement = {};                
                 $scope.currentEvent = event;
+                
+                var index = -1;
+                for (var i = 0; i < $scope.eventsByStage[event.programStage].length && index === -1; i++) {
+                    if ($scope.eventsByStage[event.programStage][i].event === event.event) {
+                        index = i;
+                    }
+                }                
+                if(index !== -1){
+                    $scope.currentEvent = $scope.eventsByStage[event.programStage][index];
+                }
+                
                 $scope.showDataEntryDiv = true;
                 $scope.showEventCreationDiv = false;
 
-                if($scope.currentEvent.notes){
-                    angular.forEach($scope.currentEvent.notes, function(note){
+                if ($scope.currentEvent.notes) {
+                    angular.forEach($scope.currentEvent.notes, function (note) {
                         note.storedDate = DateUtils.formatToHrsMins(note.storedDate);
                     });
 
-                    if($scope.currentEvent.notes.length > 0 ){
+                    if ($scope.currentEvent.notes.length > 0) {
                         $scope.currentEvent.notes = orderByFilter($scope.currentEvent.notes, '-storedDate');
                     }
                 }
-                
+
                 $scope.getDataEntryForm();
-            }               
+            }
         }
-    }; 
-    
-    $scope.switchDataEntryForm = function(){
+    };
+
+    $scope.switchDataEntryForm = function () {
         $scope.displayCustomForm = !$scope.displayCustomForm;
     };
-    
-    $scope.getDataEntryForm = function(){
-        
+
+    $scope.getDataEntryForm = function () {
+
         $scope.currentStage = $scope.stagesById[$scope.currentEvent.programStage];
         $scope.currentStageEvents = $scope.eventsByStage[$scope.currentEvent.programStage];
-        
-        angular.forEach($scope.currentStage.programStageSections, function(section){
+
+        angular.forEach($scope.currentStage.programStageSections, function (section) {
             section.open = true;
         });
 
         $scope.customForm = CustomFormService.getForProgramStage($scope.currentStage, $scope.prStDes);
         $scope.displayCustomForm = "default";
-        if($scope.customForm){
+        if ($scope.customForm) {
             $scope.displayCustomForm = "custom";
         }
-        else if($scope.currentStage.displayEventsInTable) {
+        else if ($scope.currentStage.displayEventsInTable) {
             $scope.displayCustomForm = "table";
         }
-        
+
         $scope.currentEventOriginal = angular.copy($scope.currentEvent);
 
         $scope.currentStageEventsOriginal = angular.copy($scope.currentStageEvents);
-        
+
         var period = {event: $scope.currentEvent.event, stage: $scope.currentEvent.programStage, name: $scope.currentEvent.sortingDate};
         $scope.currentPeriod[$scope.currentEvent.programStage] = period;
 
@@ -396,356 +416,327 @@
         //Subsequent calls will be made from the "saveDataValue" function.
         $scope.executeRules();
     };
-    
-    function updateCurrentEventInStage(){
-        
-        var index = -1;
-        for(var i=0; i<$scope.eventsByStage[$scope.currentEvent.programStage].length && index === -1; i++){
-            if($scope.eventsByStage[$scope.currentEvent.programStage][i].event === $scope.currentEvent.event){
-                index = i;
-            }
-        }
-        if(index !== -1){
-            $scope.eventsByStage[$scope.currentEvent.programStage].splice(index,1,$scope.currentEvent);
-        }
-        
-    };
-
-    $scope.saveDatavalue = function(prStDe,field){
-        $scope.saveDatavalueForEvent(prStDe,field,$scope.currentEvent);
-    };
-
-    $scope.saveDatavalueForEvent = function(prStDe,field,eventToSave){
+
+    $scope.saveDatavalue = function (prStDe, field) {
+        $scope.saveDatavalueForEvent(prStDe, field, $scope.currentEvent);
+    };
+
+    $scope.saveDatavalueForEvent = function (prStDe, field, eventToSave) {
         //Blank out the input-saved class on the last saved due date:
         $scope.eventDateSaved = false;
-        
+
         //console.log('the field:  ', field);
         $scope.currentElement = {};
-        
+
         //check for input validity
         //$scope.outerForm.submitted = true;            
         $scope.updateSuccess = false;
-        if( field && field.$invalid ){ 
+        if (field && field.$invalid) {
             //console.log('form is invalid...');
             $scope.currentElement = {id: prStDe.dataElement.id, saved: false};
             return false;
         }
-        
+
         //input is valid
         var value = eventToSave[prStDe.dataElement.id];
-        
+
         var oldValue = null;
         angular.forEach($scope.currentStageEventsOriginal, function (eventOriginal) {
-            if(eventOriginal.event === eventToSave.event) {
-               oldValue = eventOriginal[prStDe.dataElement.id];
+            if (eventOriginal.event === eventToSave.event) {
+                oldValue = eventOriginal[prStDe.dataElement.id];
             }
         });
-        
-        if(oldValue !== value){
-            if(value){
-                if(prStDe.dataElement.type === 'date'){                    
+
+        if (oldValue !== value) {
+            if (value) {
+                if (prStDe.dataElement.type === 'date') {
                     value = DateUtils.formatFromUserToApi(value);
                 }
-                if(prStDe.dataElement.optionSetValue){                    
-                    if(prStDe.dataElement.optionSet && $scope.optionSets[prStDe.dataElement.optionSet.id] &&  $scope.optionSets[prStDe.dataElement.optionSet.id].options ) {
+                if (prStDe.dataElement.optionSetValue) {
+                    if (prStDe.dataElement.optionSet && $scope.optionSets[prStDe.dataElement.optionSet.id] && $scope.optionSets[prStDe.dataElement.optionSet.id].options) {
                         value = OptionSetService.getCode($scope.optionSets[prStDe.dataElement.optionSet.id].options, value);
-                    }                    
+                    }
                 }
             }
 
             $scope.updateSuccess = false;
 
-            $scope.currentElement = {id: prStDe.dataElement.id, event:eventToSave.event, saved: false};
-
-            var ev = {  event: eventToSave.event,
-                        orgUnit: eventToSave.orgUnit,
-                        program: eventToSave.program,
-                        programStage: eventToSave.programStage,
-                        status: eventToSave.status,
-                        trackedEntityInstance: eventToSave.trackedEntityInstance,
-                        dataValues: [
-                                        {
-                                            dataElement: prStDe.dataElement.id, 
-                                            value: value, 
-                                            providedElsewhere: eventToSave.providedElsewhere[prStDe.dataElement.id] ? true : false
-                                        }
-                                    ]
-                     };
-            DHIS2EventFactory.updateForSingleValue(ev).then(function(response){                
-                updateCurrentEventInStage();
-                sortEventsByStage('UPDATE');
-                
+            $scope.currentElement = {id: prStDe.dataElement.id, event: eventToSave.event, saved: false};
+
+            var ev = {event: eventToSave.event,
+                orgUnit: eventToSave.orgUnit,
+                program: eventToSave.program,
+                programStage: eventToSave.programStage,
+                status: eventToSave.status,
+                trackedEntityInstance: eventToSave.trackedEntityInstance,
+                dataValues: [
+                    {
+                        dataElement: prStDe.dataElement.id,
+                        value: value,
+                        providedElsewhere: eventToSave.providedElsewhere[prStDe.dataElement.id] ? true : false
+                    }
+                ]
+            };
+            DHIS2EventFactory.updateForSingleValue(ev).then(function (response) {
+
                 $scope.currentElement.saved = true;
-                
+
                 $scope.currentEventOriginal = angular.copy($scope.currentEvent);
-                
+
                 $scope.currentStageEventsOriginal = angular.copy($scope.currentStageEvents);
 
                 //Run rules on updated data:
-		$scope.executeRules();
+                $scope.executeRules();
             });
-            
+
         }
     };
-    
-    $scope.saveDatavalueLocation = function(prStDe){
-                
+
+    $scope.saveDatavalueLocation = function (prStDe) {
+
         $scope.updateSuccess = false;
-        
-        if(!angular.isUndefined($scope.currentEvent.providedElsewhere[prStDe.dataElement.id])){
+
+        if (!angular.isUndefined($scope.currentEvent.providedElsewhere[prStDe.dataElement.id])) {
 
             //currentEvent.providedElsewhere[prStDe.dataElement.id];
             var value = $scope.currentEvent[prStDe.dataElement.id];
-            var ev = {  event: $scope.currentEvent.event,
-                        orgUnit: $scope.currentEvent.orgUnit,
-                        program: $scope.currentEvent.program,
-                        programStage: $scope.currentEvent.programStage,
-                        status: $scope.currentEvent.status,
-                        trackedEntityInstance: $scope.currentEvent.trackedEntityInstance,
-                        dataValues: [
-                                        {
-                                            dataElement: prStDe.dataElement.id, 
-                                            value: value, 
-                                            providedElsewhere: $scope.currentEvent.providedElsewhere[prStDe.dataElement.id] ? true : false
-                                        }
-                                    ]
-                     };
-            DHIS2EventFactory.updateForSingleValue(ev).then(function(response){                
-                updateCurrentEventInStage();
-                sortEventsByStage('UPDATE');
-                
+            var ev = {event: $scope.currentEvent.event,
+                orgUnit: $scope.currentEvent.orgUnit,
+                program: $scope.currentEvent.program,
+                programStage: $scope.currentEvent.programStage,
+                status: $scope.currentEvent.status,
+                trackedEntityInstance: $scope.currentEvent.trackedEntityInstance,
+                dataValues: [
+                    {
+                        dataElement: prStDe.dataElement.id,
+                        value: value,
+                        providedElsewhere: $scope.currentEvent.providedElsewhere[prStDe.dataElement.id] ? true : false
+                    }
+                ]
+            };
+            DHIS2EventFactory.updateForSingleValue(ev).then(function (response) {
                 $scope.updateSuccess = true;
             });
         }
     };
-    
+
     $scope.saveEventDate = function () {
         $scope.saveEventDateForEvent($scope.currentEvent);
     };
-    
-    $scope.saveEventDateForEvent = function(eventToSave){
+
+    $scope.saveEventDateForEvent = function (eventToSave) {
         $scope.eventDateSaved = false;
-        if(eventToSave.eventDate === ''){            
+        if (eventToSave.eventDate === '') {
             $scope.invalidDate = eventToSave.event;
             return false;
         }
-        
+
         var rawDate = angular.copy(eventToSave.eventDate);
         var convertedDate = DateUtils.format(eventToSave.eventDate);
-        
-        if(rawDate !== convertedDate){
+
+        if (rawDate !== convertedDate) {
             $scope.invalidDate = true;
             return false;
         }
-        
+
         var e = {event: eventToSave.event,
-             enrollment: eventToSave.enrollment,
-             dueDate: DateUtils.formatFromUserToApi(eventToSave.dueDate),
-             status: eventToSave.status === 'SCHEDULE' ? 'ACTIVE' : eventToSave.status,
-             program: eventToSave.program,
-             programStage: eventToSave.programStage,
-             orgUnit: eventToSave.dataValues && eventToSave.length > 0 ? eventToSave.orgUnit : $scope.selectedOrgUnit.id,
-             eventDate: DateUtils.formatFromUserToApi(eventToSave.eventDate),
-             trackedEntityInstance: eventToSave.trackedEntityInstance
-            };
+            enrollment: eventToSave.enrollment,
+            dueDate: DateUtils.formatFromUserToApi(eventToSave.dueDate),
+            status: eventToSave.status === 'SCHEDULE' ? 'ACTIVE' : eventToSave.status,
+            program: eventToSave.program,
+            programStage: eventToSave.programStage,
+            orgUnit: eventToSave.dataValues && eventToSave.length > 0 ? eventToSave.orgUnit : $scope.selectedOrgUnit.id,
+            eventDate: DateUtils.formatFromUserToApi(eventToSave.eventDate),
+            trackedEntityInstance: eventToSave.trackedEntityInstance
+        };
 
-        DHIS2EventFactory.updateForEventDate(e).then(function(data){
+        DHIS2EventFactory.updateForEventDate(e).then(function (data) {
             eventToSave.sortingDate = eventToSave.eventDate;
             $scope.invalidDate = false;
             $scope.eventDateSaved = eventToSave.event;
-            eventToSave.statusColor = EventUtils.getEventStatusColor(eventToSave);
-            sortEventsByStage();
-            updateCurrentEventInStage();
+            eventToSave.statusColor = EventUtils.getEventStatusColor(eventToSave); 
             sortEventsByStage('UPDATE');
         });
     };
-    
-    $scope.saveDueDate = function(){
-        
+
+    $scope.saveDueDate = function () {
+
         $scope.dueDateSaved = false;
 
-        if($scope.currentEvent.dueDate === ''){
+        if ($scope.currentEvent.dueDate === '') {
             $scope.invalidDate = true;
             return false;
         }
-        
+
         var rawDate = angular.copy($scope.currentEvent.dueDate);
-        var convertedDate = DateUtils.format($scope.currentEvent.dueDate);           
+        var convertedDate = DateUtils.format($scope.currentEvent.dueDate);
 
-        if(rawDate !== convertedDate){
+        if (rawDate !== convertedDate) {
             $scope.invalidDate = true;
             return false;
-        } 
-        
+        }
+
         var e = {event: $scope.currentEvent.event,
-             enrollment: $scope.currentEvent.enrollment,
-             dueDate: DateUtils.formatFromUserToApi($scope.currentEvent.dueDate),
-             status: $scope.currentEvent.status,
-             program: $scope.currentEvent.program,
-             programStage: $scope.currentEvent.programStage,
-             orgUnit: $scope.selectedOrgUnit.id,
-             trackedEntityInstance: $scope.currentEvent.trackedEntityInstance
-            };
-        
-        if($scope.currentStage.periodType){
+            enrollment: $scope.currentEvent.enrollment,
+            dueDate: DateUtils.formatFromUserToApi($scope.currentEvent.dueDate),
+            status: $scope.currentEvent.status,
+            program: $scope.currentEvent.program,
+            programStage: $scope.currentEvent.programStage,
+            orgUnit: $scope.selectedOrgUnit.id,
+            trackedEntityInstance: $scope.currentEvent.trackedEntityInstance
+        };
+
+        if ($scope.currentStage.periodType) {
             e.eventDate = e.dueDate;
         }
-        
-        if($scope.currentEvent.coordinate){
+
+        if ($scope.currentEvent.coordinate) {
             e.coordinate = $scope.currentEvent.coordinate;
         }
-            
-        DHIS2EventFactory.update(e).then(function(data){            
+
+        DHIS2EventFactory.update(e).then(function (data) {
             $scope.invalidDate = false;
             $scope.dueDateSaved = true;
-            
-            if(e.eventDate && !$scope.currentEvent.eventDate && $scope.currentStage.periodType){
+
+            if (e.eventDate && !$scope.currentEvent.eventDate && $scope.currentStage.periodType) {
                 $scope.currentEvent.eventDate = $scope.currentEvent.dueDate;
             }
-            
-            $scope.currentEvent.sortingDate = $scope.currentEvent.dueDate;            
-            $scope.currentEvent.statusColor = EventUtils.getEventStatusColor($scope.currentEvent);            
+
+            $scope.currentEvent.sortingDate = $scope.currentEvent.dueDate;
+            $scope.currentEvent.statusColor = EventUtils.getEventStatusColor($scope.currentEvent);
             $scope.schedulingEnabled = !$scope.schedulingEnabled;
-            
-            updateCurrentEventInStage();
             sortEventsByStage('UPDATE');
         });
-                      
+
     };
-    
-    $scope.saveCoordinate = function(type){
-        
-        if(type === 'LAT' || type === 'LATLNG' ){
+
+    $scope.saveCoordinate = function (type) {
+
+        if (type === 'LAT' || type === 'LATLNG') {
             $scope.latitudeSaved = false;
         }
-        if(type === 'LAT' || type === 'LATLNG'){
+        if (type === 'LAT' || type === 'LATLNG') {
             $scope.longitudeSaved = false;
         }
-        
-        if( (type === 'LAT' || type === 'LATLNG') && $scope.outerForm.latitude.$invalid  || 
-            (type === 'LNG' || type === 'LATLNG') && $scope.outerForm.longitude.$invalid ){//invalid coordinate            
-            return;            
-        }
-        
-        if( (type === 'LAT' || type === 'LATLNG') && $scope.currentEvent.coordinate.latitude === $scope.currentEventOriginal.coordinate.latitude  || 
-            (type === 'LNG' || type === 'LATLNG') && $scope.currentEvent.coordinate.longitude === $scope.currentEventOriginal.coordinate.longitude){//no change            
-            return;            
-        }
-        
+
+        if ((type === 'LAT' || type === 'LATLNG') && $scope.outerForm.latitude.$invalid ||
+                (type === 'LNG' || type === 'LATLNG') && $scope.outerForm.longitude.$invalid) {//invalid coordinate            
+            return;
+        }
+
+        if ((type === 'LAT' || type === 'LATLNG') && $scope.currentEvent.coordinate.latitude === $scope.currentEventOriginal.coordinate.latitude ||
+                (type === 'LNG' || type === 'LATLNG') && $scope.currentEvent.coordinate.longitude === $scope.currentEventOriginal.coordinate.longitude) {//no change            
+            return;
+        }
+
         //valid coordinate(s), proceed with the saving
         var dhis2Event = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);
-        
-        DHIS2EventFactory.update(dhis2Event).then(function(response){            
+
+        DHIS2EventFactory.update(dhis2Event).then(function (response) {
             $scope.currentEventOriginal = angular.copy($scope.currentEvent);
             $scope.currentStageEventsOriginal = angular.copy($scope.currentStageEvents);
 
-            if(type === 'LAT' || type === 'LATLNG' ){
+            if (type === 'LAT' || type === 'LATLNG') {
                 $scope.latitudeSaved = true;
             }
-            if(type === 'LAT' || type === 'LATLNG'){
+            if (type === 'LAT' || type === 'LATLNG') {
                 $scope.longitudeSaved = true;
             }
-            
-            updateCurrentEventInStage();
-            sortEventsByStage('UPDATE');
         });
     };
-    
-    $scope.addNote = function(){
-        if(!angular.isUndefined($scope.note) && $scope.note !== ""){
+
+    $scope.addNote = function () {
+        if (!angular.isUndefined($scope.note) && $scope.note !== "") {
             var newNote = {value: $scope.note};
 
-            if(angular.isUndefined( $scope.currentEvent.notes) ){
+            if (angular.isUndefined($scope.currentEvent.notes)) {
                 $scope.currentEvent.notes = [{value: $scope.note, storedDate: today, storedBy: storedBy}];
             }
-            else{
-                $scope.currentEvent.notes.splice(0,0,{value: $scope.note, storedDate: today, storedBy: storedBy});
+            else {
+                $scope.currentEvent.notes.splice(0, 0, {value: $scope.note, storedDate: today, storedBy: storedBy});
             }
 
             var e = {event: $scope.currentEvent.event,
-                     program: $scope.currentEvent.program,
-                     programStage: $scope.currentEvent.programStage,
-                     orgUnit: $scope.currentEvent.orgUnit,
-                     trackedEntityInstance: $scope.currentEvent.trackedEntityInstance,
-                     notes: [newNote]
-                    };
+                program: $scope.currentEvent.program,
+                programStage: $scope.currentEvent.programStage,
+                orgUnit: $scope.currentEvent.orgUnit,
+                trackedEntityInstance: $scope.currentEvent.trackedEntityInstance,
+                notes: [newNote]
+            };
 
-            DHIS2EventFactory.updateForNote(e).then(function(data){
-                $scope.note = ''; 
-                
-                updateCurrentEventInStage();
-                sortEventsByStage('UPDATE');
+            DHIS2EventFactory.updateForNote(e).then(function (data) {
+                $scope.note = '';
             });
-        }        
-    };    
-    
-    $scope.clearNote = function(){
-         $scope.note = '';           
-    };
-    
-    $scope.getInputDueDateClass = function(event) {
-        if(event.event === $scope.eventDateSaved) {
+        }
+    };
+
+    $scope.clearNote = function () {
+        $scope.note = '';
+    };
+
+    $scope.getInputDueDateClass = function (event) {
+        if (event.event === $scope.eventDateSaved) {
             return 'input-success';
         }
         else {
             return '';
         }
-            
+
     };
-    
+
     /*$scope.getInputNotifcationClass = function(id, custom, event){
-        if(!event) {
-            event = $scope.currentEvent;
-        }
-        if($scope.currentElement.id && $scope.currentElement.event){
-            if($scope.currentElement.saved && ($scope.currentElement.id === id && $scope.currentElement.event === event.event)){
-                
-                if(custom){
-                    return 'input-success';
-                }
-                return 'form-control input-success';
-            }            
-            if(!$scope.currentElement.saved && ($scope.currentElement.id === id && $scope.currentElement.event === event.event)){
-                if(custom){
-                    return 'input-error';
-                }
-                return 'form-control input-error';
-            }            
-        }  
-        if(custom){
-            return '';
-        }
-        return 'form-control';
-    };*/
-            
+     if(!event) {
+     event = $scope.currentEvent;
+     }
+     if($scope.currentElement.id && $scope.currentElement.event){
+     if($scope.currentElement.saved && ($scope.currentElement.id === id && $scope.currentElement.event === event.event)){
+
+     if(custom){
+     return 'input-success';
+     }
+     return 'form-control input-success';
+     }            
+     if(!$scope.currentElement.saved && ($scope.currentElement.id === id && $scope.currentElement.event === event.event)){
+     if(custom){
+     return 'input-error';
+     }
+     return 'form-control input-error';
+     }            
+     }  
+     if(custom){
+     return '';
+     }
+     return 'form-control';
+     };*/
+
     //Infinite Scroll
     $scope.infiniteScroll = {};
     $scope.infiniteScroll.optionsToAdd = 20;
     $scope.infiniteScroll.currentOptions = 20;
-    
-    $scope.resetInfScroll = function() {
+
+    $scope.resetInfScroll = function () {
         $scope.infiniteScroll.currentOptions = $scope.infiniteScroll.optionsToAdd;
     };
-  
-    $scope.addMoreOptions = function(){
+
+    $scope.addMoreOptions = function () {
         $scope.infiniteScroll.currentOptions += $scope.infiniteScroll.optionsToAdd;
-    };        
-    
-    $scope.getInputNotifcationClass = function(id, custom, event){
-        if(!event) {
+    };
+
+    $scope.getInputNotifcationClass = function (id, custom, event) {
+        if (!event) {
             event = $scope.currentEvent;
         }
-        if($scope.currentElement.id && 
-                $scope.currentElement.event && 
+        if ($scope.currentElement.id &&
+                $scope.currentElement.event &&
                 $scope.currentElement.id === id &&
-                $scope.currentElement.event === event.event){            
-            return $scope.currentElement.saved ? 'input-success; ' : 'input-error; ';                      
-        }  
-        
+                $scope.currentElement.event === event.event) {
+            return $scope.currentElement.saved ? 'input-success; ' : 'input-error; ';
+        }
+
         return '';
     };
-    
-    var completeEnrollment = function(){
+
+    var completeEnrollment = function () {
         var modalOptions = {
             closeButtonText: 'cancel',
             actionButtonText: 'complete',
@@ -753,26 +744,26 @@
             bodyText: 'would_you_like_to_complete_enrollment'
         };
 
-        ModalService.showModal({}, modalOptions).then(function(result){            
-            EnrollmentService.complete($scope.selectedEnrollment).then(function(data){                
-                $scope.selectedEnrollment.status = 'COMPLETED';            
+        ModalService.showModal({}, modalOptions).then(function (result) {
+            EnrollmentService.complete($scope.selectedEnrollment).then(function (data) {
+                $scope.selectedEnrollment.status = 'COMPLETED';
             });
         });
     };
-    
-    $scope.completeIncompleteEvent = function(){
+
+    $scope.completeIncompleteEvent = function () {
         var modalOptions;
-        var dhis2Event = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);        
-        if($scope.currentEvent.status === 'COMPLETED'){//activiate event
+        var dhis2Event = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);
+        if ($scope.currentEvent.status === 'COMPLETED') {//activiate event
             modalOptions = {
                 closeButtonText: 'cancel',
                 actionButtonText: 'incomplete',
                 headerText: 'incomplete',
                 bodyText: 'are_you_sure_to_incomplete_event'
             };
-            dhis2Event.status = 'ACTIVE';        
+            dhis2Event.status = 'ACTIVE';
         }
-        else{//complete event
+        else {//complete event
             modalOptions = {
                 closeButtonText: 'cancel',
                 actionButtonText: 'complete',
@@ -780,54 +771,52 @@
                 bodyText: 'are_you_sure_to_complete_event'
             };
             dhis2Event.status = 'COMPLETED';
-        }        
-
-        ModalService.showModal({}, modalOptions).then(function(result){
-            
-            DHIS2EventFactory.update(dhis2Event).then(function(data){
-                
-                if($scope.currentEvent.status === 'COMPLETED'){//activiate event                    
-                    $scope.currentEvent.status = 'ACTIVE'; 
+        }
+
+        ModalService.showModal({}, modalOptions).then(function (result) {
+
+            DHIS2EventFactory.update(dhis2Event).then(function (data) {
+
+                if ($scope.currentEvent.status === 'COMPLETED') {//activiate event                    
+                    $scope.currentEvent.status = 'ACTIVE';
                 }
-                else{//complete event                    
+                else {//complete event                    
                     $scope.currentEvent.status = 'COMPLETED';
                 }
-                
+
                 setStatusColor();
-                updateCurrentEventInStage();
-                sortEventsByStage('UPDATE');
-                
+
                 setEventEditing($scope.currentEvent, $scope.currentStage);
-                
-                if($scope.currentEvent.status === 'COMPLETED'){
-                    
-                    if($scope.currentStage.remindCompleted){
+
+                if ($scope.currentEvent.status === 'COMPLETED') {
+
+                    if ($scope.currentStage.remindCompleted) {
                         completeEnrollment($scope.currentStage);
                     }
-                    else{
-                        if($scope.currentStage.allowGenerateNextVisit){
+                    else {
+                        if ($scope.currentStage.allowGenerateNextVisit) {
                             $scope.showCreateEvent($scope.currentStage);
                         }
                     }
-                }                
+                }
             });
         });
     };
-    
-    $scope.skipUnskipEvent = function(){
+
+    $scope.skipUnskipEvent = function () {
         var modalOptions;
-        var dhis2Event = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);   
+        var dhis2Event = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);
 
-        if($scope.currentEvent.status === 'SKIPPED'){//unskip event
+        if ($scope.currentEvent.status === 'SKIPPED') {//unskip event
             modalOptions = {
                 closeButtonText: 'cancel',
                 actionButtonText: 'unskip',
                 headerText: 'unskip',
                 bodyText: 'are_you_sure_to_unskip_event'
             };
-            dhis2Event.status = 'ACTIVE';        
+            dhis2Event.status = 'ACTIVE';
         }
-        else{//skip event
+        else {//skip event
             modalOptions = {
                 closeButtonText: 'cancel',
                 actionButtonText: 'skip',
@@ -835,43 +824,42 @@
                 bodyText: 'are_you_sure_to_skip_event'
             };
             dhis2Event.status = 'SKIPPED';
-        }        
-
-        ModalService.showModal({}, modalOptions).then(function(result){
-            
-            DHIS2EventFactory.update(dhis2Event).then(function(data){
-                
-                if($scope.currentEvent.status === 'SKIPPED'){//activiate event                    
-                    $scope.currentEvent.status = 'SCHEDULE'; 
+        }
+
+        ModalService.showModal({}, modalOptions).then(function (result) {
+
+            DHIS2EventFactory.update(dhis2Event).then(function (data) {
+
+                if ($scope.currentEvent.status === 'SKIPPED') {//activiate event                    
+                    $scope.currentEvent.status = 'SCHEDULE';
                 }
-                else{//complete event                    
+                else {//complete event                    
                     $scope.currentEvent.status = 'SKIPPED';
                 }
-                
+
                 setStatusColor();
                 setEventEditing($scope.currentEvent, $scope.currentStage);
-                updateCurrentEventInStage();
-                sortEventsByStage('UPDATE');
             });
         });
     };
-    
-    var setStatusColor = function(){
-        var statusColor = EventUtils.getEventStatusColor($scope.currentEvent);  
+
+    var setStatusColor = function () {
+        var statusColor = EventUtils.getEventStatusColor($scope.currentEvent);
         var continueLoop = true;
-        for(var i=0; i< $scope.eventsByStage[$scope.currentEvent.programStage].length && continueLoop; i++){
-            if($scope.eventsByStage[$scope.currentEvent.programStage][i].event === $scope.currentEvent.event ){
+        for (var i = 0; i < $scope.eventsByStage[$scope.currentEvent.programStage].length && continueLoop; i++) {
+            if ($scope.eventsByStage[$scope.currentEvent.programStage][i].event === $scope.currentEvent.event) {
                 $scope.eventsByStage[$scope.currentEvent.programStage][i].statusColor = statusColor;
                 $scope.currentEvent.statusColor = statusColor;
                 continueLoop = false;
             }
         }
     };
-    
-    $scope.validateEvent = function(){};    
-    
-    $scope.deleteEvent = function(){
-        
+
+    $scope.validateEvent = function () {
+    };
+
+    $scope.deleteEvent = function () {
+
         var modalOptions = {
             closeButtonText: 'cancel',
             actionButtonText: 'delete',
@@ -879,142 +867,145 @@
             bodyText: 'are_you_sure_to_delete_event'
         };
 
-        ModalService.showModal({}, modalOptions).then(function(result){
-            
-            DHIS2EventFactory.delete($scope.currentEvent).then(function(data){
-                
+        ModalService.showModal({}, modalOptions).then(function (result) {
+
+            DHIS2EventFactory.delete($scope.currentEvent).then(function (data) {
+
                 var continueLoop = true, index = -1;
-                for(var i=0; i< $scope.eventsByStage[$scope.currentEvent.programStage].length && continueLoop; i++){
-                    if($scope.eventsByStage[$scope.currentEvent.programStage][i].event === $scope.currentEvent.event ){
+                for (var i = 0; i < $scope.eventsByStage[$scope.currentEvent.programStage].length && continueLoop; i++) {
+                    if ($scope.eventsByStage[$scope.currentEvent.programStage][i].event === $scope.currentEvent.event) {
                         $scope.eventsByStage[$scope.currentEvent.programStage][i] = $scope.currentEvent;
                         continueLoop = false;
                         index = i;
                     }
                 }
-                $scope.eventsByStage[$scope.currentEvent.programStage].splice(index,1);                
+                $scope.eventsByStage[$scope.currentEvent.programStage].splice(index, 1);
                 sortEventsByStage('REMOVE');
                 $scope.currentEvent = null;
             });
         });
     };
-    
-    $scope.toggleLegend = function(){
+
+    $scope.toggleLegend = function () {
         $scope.showEventColors = !$scope.showEventColors;
     };
-    
-    $scope.getEventStyle = function(ev){
+
+    $scope.getEventStyle = function (ev) {
         var style = EventUtils.getEventStatusColor(ev);
-        
-        if($scope.currentEvent && $scope.currentEvent.event === ev.event){
-            style = style + ' ' + 'current-stage';
-        }       
+
+        if ($scope.currentEvent && $scope.currentEvent.event === ev.event) {
+            style = style + ' ' + ' current-stage';
+        }
         return style;
     };
-    
-    $scope.getColumnWidth = function(weight){        
+
+    $scope.getColumnWidth = function (weight) {
         var width = weight <= 1 ? 1 : weight;
-        width = (width/$scope.totalEvents)*100;
+        width = (width / $scope.totalEvents) * 100;
         return "width: " + width + '%';
     };
-    
-    $scope.sortEventsByDate = function(dhis2Event){
-        var d = dhis2Event.sortingDate;         
-        return DateUtils.getDate(d);                
+
+    $scope.sortEventsByDate = function (dhis2Event) {
+        var d = dhis2Event.sortingDate;
+        return DateUtils.getDate(d);
     };
-    
-    var sortEventsByStage = function(operation){
-        
+
+    var sortEventsByStage = function (operation) {
+
         $scope.eventFilteringRequired = false;
-        
-        for(var key in $scope.eventsByStage){
-            
+
+        for (var key in $scope.eventsByStage) {
+
             var stage = $scope.stagesById[key];
-            
-            if($scope.eventsByStage.hasOwnProperty(key) && stage){                
-                
-                var sortedEvents = $filter('orderBy')($scope.eventsByStage[key], function(event) {
+
+            if ($scope.eventsByStage.hasOwnProperty(key) && stage) {
+
+                var sortedEvents = $filter('orderBy')($scope.eventsByStage[key], function (event) {
                     return DateUtils.getDate(event.sortingDate);
                 }, true);
-                
+
                 $scope.eventsByStage[key] = sortedEvents;
-                
+                $scope.eventsByStageAsc[key] = angular.copy(angular.copy(sortedEvents).reverse());
+
                 var periods = PeriodService.getPeriods(sortedEvents, stage, $scope.selectedEnrollment).occupiedPeriods;
-                
+
                 $scope.eventPeriods[key] = periods;
-                $scope.currentPeriod[key] = periods.length > 0 ? periods[0] : null;  
-                $scope.eventFilteringRequired = $scope.eventFilteringRequired ? $scope.eventFilteringRequired : periods.length > 1;                
+                $scope.currentPeriod[key] = periods.length > 0 ? periods[0] : null;
+                $scope.eventFilteringRequired = $scope.eventFilteringRequired ? $scope.eventFilteringRequired : periods.length > 1;
             }
         }
         
-        if(operation !== null){
-            
-            var evs = CurrentSelection.getSelectedTeiEvents();
-            
-            if( operation ===  'ADD' ){
-                var ev = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);
-                ev.enrollment = $scope.currentEvent.enrollment;
-                ev.visited = $scope.currentEvent.visited;
-                evs.push(ev);
-            }   
-            if( operation === 'UPDATE' ){                
-                var ev = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);
-                ev.enrollment = $scope.currentEvent.enrollment;
-                ev.visited = $scope.currentEvent.visited;
-                var index = -1;
-                for(var i=0; i<evs.length && index === -1; i++){
-                    if(evs[i].event === $scope.currentEvent.event){
-                        index = i;
-                    }
-                }
-                if(index !== -1){
-                    evs[index] = ev;
-                }            
-            }
-            if( operation === 'REMOVE' ){
-                var index = -1;
-                for(var i=0; i<evs.length && index === -1; i++){
-                    if(evs[i].event === $scope.currentEvent.event){
-                        index = i;
-                    }
-                }
-                if(index !== -1){
-                    evs.splice(index,1);
-                }                        
-            }
-            
-            CurrentSelection.setSelectedTeiEvents( evs );
-            
-            $timeout(function() { 
-                $rootScope.$broadcast('tei-report-widget', {});            
+        $scope.allEventsSorted = CurrentSelection.getSelectedTeiEvents();
+        
+        if (operation) {
+
+            if (operation === 'ADD') {
+                var ev = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);
+                ev.enrollment = $scope.currentEvent.enrollment;
+                ev.visited = $scope.currentEvent.visited;
+                $scope.allEventsSorted.push(ev);
+            }
+            if (operation === 'UPDATE') {
+                var ev = EventUtils.reconstruct($scope.currentEvent, $scope.currentStage, $scope.optionSets);
+                ev.enrollment = $scope.currentEvent.enrollment;
+                ev.visited = $scope.currentEvent.visited;
+                var index = -1;
+                for (var i = 0; i < $scope.allEventsSorted.length && index === -1; i++) {
+                    if ($scope.allEventsSorted[i].event === $scope.currentEvent.event) {
+                        index = i;
+                    }
+                }
+                if (index !== -1) {
+                    $scope.allEventsSorted[index] = ev;
+                }
+            }
+            if (operation === 'REMOVE') {
+                var index = -1;
+                for (var i = 0; i < $scope.allEventsSorted.length && index === -1; i++) {
+                    if ($scope.allEventsSorted[i].event === $scope.currentEvent.event) {
+                        index = i;
+                    }
+                }
+                if (index !== -1) {
+                    $scope.allEventsSorted.splice(index, 1);
+                }
+            }
+
+            CurrentSelection.setSelectedTeiEvents($scope.allEventsSorted);
+
+            $timeout(function () {
+                $rootScope.$broadcast('tei-report-widget', {});
             }, 100);
-        }        
+        }
+        
+        $scope.allEventsSorted = orderByFilter($scope.allEventsSorted, '-sortingDate').reverse();
     };
-    
-    $scope.showLastEventInStage = function(stageId){
-        var ev = $scope.eventsByStage[stageId][$scope.eventsByStage[stageId].length-1];
+
+    $scope.showLastEventInStage = function (stageId) {
+        var ev = $scope.eventsByStage[stageId][$scope.eventsByStage[stageId].length - 1];
         $scope.showDataEntryForEvent(ev);
     };
-    
-    $scope.showDataEntryForEvent = function(event){
-        
+
+    $scope.showDataEntryForEvent = function (event) {
+
         var period = {event: event.event, stage: event.programStage, name: event.sortingDate};
         $scope.currentPeriod[event.programStage] = period;
-        
+
         var event = null;
-        for(var i=0; i<$scope.eventsByStage[period.stage].length; i++){
-            if($scope.eventsByStage[period.stage][i].event === period.event){
+        for (var i = 0; i < $scope.eventsByStage[period.stage].length; i++) {
+            if ($scope.eventsByStage[period.stage][i].event === period.event) {
                 event = $scope.eventsByStage[period.stage][i];
                 break;
             }
         }
-        
-        if(event){
+
+        if (event) {
             $scope.showDataEntry(event, false);
         }
-        
+
     };
-    
-    $scope.showMap = function(event){
+
+    $scope.showMap = function (event) {
         var modalInstance = $modal.open({
             templateUrl: '../dhis-web-commons/angular-forms/map.html',
             controller: 'MapController',
@@ -1027,105 +1018,105 @@
         });
 
         modalInstance.result.then(function (location) {
-            if(angular.isObject(location)){
+            if (angular.isObject(location)) {
                 event.coordinate.latitude = location.lat;
-                event.coordinate.longitude = location.lng;                
+                event.coordinate.longitude = location.lng;
                 $scope.saveCoordinate('LATLNG');
             }
         }, function () {
         });
     };
-    
-    $scope.interacted = function(field) {
+
+    $scope.interacted = function (field) {
         var status = false;
-        if(field){            
+        if (field) {
             status = $scope.outerForm.submitted || field.$dirty;
-        }        
-        return status;        
+        }
+        return status;
     };
-    
+
 })
 
-.controller('EventCreationController', 
-    function($scope, 
-            $modalInstance, 
-            DateUtils,
-            DHIS2EventFactory,
-            DialogService,
-            stagesById,
-            dummyEvent,
-            eventPeriods,
-            autoCreate){
+.controller('EventCreationController',
+        function ($scope,
+                $modalInstance,
+                DateUtils,
+                DHIS2EventFactory,
+                DialogService,
+                stagesById,
+                dummyEvent,
+                eventPeriods,
+                autoCreate) {
     $scope.stagesById = stagesById;
     $scope.programStageId = dummyEvent.programStage;
     $scope.eventPeriods = eventPeriods;
-    $scope.selectedStage =  $scope.stagesById[dummyEvent.programStage];
-    
+    $scope.selectedStage = $scope.stagesById[dummyEvent.programStage];
+
     $scope.dhis2Event = {eventDate: '', dueDate: dummyEvent.dueDate, reportDateDescription: dummyEvent.reportDateDescription, name: dummyEvent.name, invalid: true};
-    
-    if($scope.selectedStage.periodType){
+
+    if ($scope.selectedStage.periodType) {
         $scope.dhis2Event.eventDate = dummyEvent.dueDate;
         $scope.dhis2Event.periodName = dummyEvent.periodName;
         $scope.dhis2Event.periods = dummyEvent.periods;
         $scope.dhis2Event.selectedPeriod = dummyEvent.periods[0];
     }
-    
+
     $scope.dueDateInvalid = false;
     $scope.eventDateInvalid = false;
-    
+
     //watch for changes in due/event-date
-    $scope.$watchCollection('[dhis2Event.dueDate, dhis2Event.eventDate]', function() {        
-        if(angular.isObject($scope.dhis2Event)){
-            if(!$scope.dhis2Event.dueDate){
+    $scope.$watchCollection('[dhis2Event.dueDate, dhis2Event.eventDate]', function () {
+        if (angular.isObject($scope.dhis2Event)) {
+            if (!$scope.dhis2Event.dueDate) {
                 $scope.dueDateInvalid = true;
                 return;
             }
-            
-            if($scope.dhis2Event.dueDate){
+
+            if ($scope.dhis2Event.dueDate) {
                 var rDueDate = $scope.dhis2Event.dueDate;
-                var cDueDate = DateUtils.format($scope.dhis2Event.dueDate);                
+                var cDueDate = DateUtils.format($scope.dhis2Event.dueDate);
                 $scope.dueDateInvalid = rDueDate !== cDueDate;
             }
-            
-            if($scope.dhis2Event.eventDate){
+
+            if ($scope.dhis2Event.eventDate) {
                 var rEventDate = $scope.dhis2Event.eventDate;
                 var cEventDate = DateUtils.format($scope.dhis2Event.eventDate);
                 $scope.eventDateInvalid = rEventDate !== cEventDate;
             }
         }
     });
-    
+
     $scope.save = function () {
         //check for form validity
-        if($scope.dueDateInvalid || $scope.eventDateInvalid){
+        if ($scope.dueDateInvalid || $scope.eventDateInvalid) {
             return false;
         }
-        
-        if($scope.selectedStage.periodType){
+
+        if ($scope.selectedStage.periodType) {
             $scope.dhis2Event.eventDate = $scope.dhis2Event.selectedPeriod.endDate;
             $scope.dhis2Event.dueDate = $scope.dhis2Event.selectedPeriod.endDate;
-        }        
-        
+        }
+
         var eventDate = DateUtils.formatFromUserToApi($scope.dhis2Event.eventDate);
         var dueDate = DateUtils.formatFromUserToApi($scope.dhis2Event.dueDate);
         var newEvents = {events: []};
         var newEvent = {
-                trackedEntityInstance: dummyEvent.trackedEntityInstance,
-                program: dummyEvent.program,
-                programStage: dummyEvent.programStage,
-                enrollment: dummyEvent.enrollment,
-                orgUnit: dummyEvent.orgUnit,                        
-                dueDate: dueDate,
-                eventDate: eventDate,
-                notes: [],
-                dataValues: [],
-                status: 'ACTIVE'
-            };            
-        
+            trackedEntityInstance: dummyEvent.trackedEntityInstance,
+            program: dummyEvent.program,
+            programStage: dummyEvent.programStage,
+            enrollment: dummyEvent.enrollment,
+            orgUnit: dummyEvent.orgUnit,
+            dueDate: dueDate,
+            eventDate: eventDate,
+            notes: [],
+            dataValues: [],
+            status: 'ACTIVE'
+        };
+
         newEvent.status = newEvent.eventDate ? 'ACTIVE' : 'SCHEDULE';
-        
+
         newEvents.events.push(newEvent);
-        DHIS2EventFactory.create(newEvents).then(function(data){
+        DHIS2EventFactory.create(newEvents).then(function (data) {
             if (data.importSummaries[0].status === 'ERROR') {
                 var dialogOptions = {
                     headerText: 'event_creation_error',
@@ -1135,20 +1126,21 @@
                 DialogService.showDialog({}, dialogOptions);
             }
             else {
-                newEvent.event = data.importSummaries[0].reference;                
+                newEvent.event = data.importSummaries[0].reference;
                 $modalInstance.close(newEvent);
             }
         });
-        
-        
+
+
     };
-    
+
     //If the caller wants to create right away, go ahead and save.
-    if(autoCreate) {
+    if (autoCreate) {
         $scope.save();
-    };
-    
-    $scope.cancel = function(){
+    }
+    ;
+
+    $scope.cancel = function () {
         $modalInstance.close();
-    };      
+    };
 });

=== 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-06-24 06:56:16 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js	2015-07-01 06:24:47 +0000
@@ -666,7 +666,7 @@
         },
         search: function(ouId, ouMode, queryUrl, programUrl, attributeUrl, pager, paging) {
                 
-            var url =  '../api/trackedEntityInstances.json?ou=' + ouId + '&ouMode='+ ouMode;
+            var url =  '../api/trackedEntityInstances/query.json?ou=' + ouId + '&ouMode='+ ouMode;
             
             if(queryUrl){
                 url = url + '&'+ queryUrl;
@@ -1098,23 +1098,9 @@
     };        
 })
 
-    /* Returns a function for getting rules for a specific program */
-.factory('TrackerRulesFactory', function($q,$rootScope,TCStorageService){
-    return{
-        getOldProgramStageRules :function(programUid, programstageUid) {
-            var rules = this.getProgramRules(programUid);
-            
-            //Only keep the rules actually matching the program stage we are in, or rules with no program stage defined.
-            var programStageRules = [];
-            angular.forEach(rules, function(rule) {
-                if(rule.programstage_uid == null || rule.programstage_uid == "" || rule.programstage_uid == programstageUid) {
-                   programStageRules.push(rule);
-                }
-            });
-            
-            return programStageRules;
-        },
-        
+/* Returns a function for getting rules for a specific program */
+.factory('TrackerRulesFactory', function($q,$rootScope,TCStorageService, MetaDataFactory){
+    return{        
         getProgramStageRules : function(programUid, programStageUid){
             var def = $q.defer();
             
@@ -1124,8 +1110,8 @@
                     var programRulesArray = [];
                     //Loop through and add the rules belonging to this program and program stage
                     angular.forEach(rules, function(rule){
-                       if(rule.program.id == programUid) {
-                           if(!rule.programStage || !rule.programStage.id || rule.programStage.id == programStageUid) {
+                       if(rule.program.id === programUid) {
+                           if(!rule.programStage || !rule.programStage.id || rule.programStage.id === programStageUid) {
                                 rule.actions = [];
                                 programRulesArray.push(rule);
                             }
@@ -1139,74 +1125,96 @@
             });
                         
             return def.promise;
+        },        
+        getRules : function(programUid){            
+            var def = $q.defer();            
+            MetaDataFactory.getAll('constants').then(function(constants) {
+                MetaDataFactory.getByProgram('programIndicators',programUid).then(function(pis){                    
+                    var variables = [];
+                    var programRules = [];
+                    angular.forEach(pis, function(pi){                    
+                        var newAction = {
+                                id:pi.id,
+                                content:pi.displayDescription ? pi.displayDescription : pi.name,
+                                data:pi.expression,
+                                programRuleActionType:'DISPLAYKEYVALUEPAIR',
+                                location:'indicators'
+                            };
+                        var newRule = {
+                                name:pi.name,
+                                id: pi.id,
+                                shortname:pi.shortname,
+                                code:pi.code,
+                                program:pi.program,
+                                description:pi.description,
+                                condition:pi.filter ? pi.filter : 'true',
+                                programRuleActions: [newAction]
+                            };
+
+                        programRules.push(newRule);
+
+                        var variablesInCondition = newRule.condition.match(/#{\w+.?\w*}/g);
+                        var variablesInData = newAction.data.match(/#{\w+.?\w*}/g);
+
+                        var pushDirectAddressedVariable = function(variableWithCurls) {
+                            var variableName = variableWithCurls.replace("#{","").replace("}","");
+                            var variableNameParts = variableName.split('.');
+
+
+                            if(variableNameParts.length === 2) {
+                                //this is a programstage and dataelement specification. translate to program variable:
+                                variables.push({
+                                    name:variableName,
+                                    programRuleVariableSourceType:'DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE',
+                                    dataElement:variableNameParts[1],
+                                    programStage:variableNameParts[0],
+                                    program:programUid
+                                });
+                            }
+                            else if(variableNameParts.length === 1)
+                            {
+                                //This is an attribute - let us translate to program variable:
+                                variables.push({
+                                    name:variableName,
+                                    programRuleVariableSourceType:'TEI_ATTRIBUTE',
+                                    trackedEntityAttribute:variableNameParts[0],
+                                    program:programUid
+                                });
+                            }
+
+                        };
+
+                        angular.forEach(variablesInCondition, function(variableInCondition) {
+                            pushDirectAddressedVariable(variableInCondition);
+                        });
+
+                        angular.forEach(variablesInData, function(variableInData) {
+                            pushDirectAddressedVariable(variableInData);
+                        });
+                    });
+
+                    var programIndicators = {rules:programRules, variables:variables};
+                    
+                    MetaDataFactory.getByProgram('programValidations',programUid).then(function(programValidations){                    
+                        MetaDataFactory.getByProgram('programRuleVariables',programUid).then(function(programVariables){                    
+                            MetaDataFactory.getByProgram('programRules',programUid).then(function(prs){
+                                var programRules = [];
+                                angular.forEach(prs, function(rule){
+                                    rule.actions = [];
+                                    rule.programStageId = rule.programStage && rule.programStage.id ? rule.programStage.id : null;
+                                    programRules.push(rule);
+                                });                                
+                                def.resolve({constants: constants, programIndicators: programIndicators, programValidations: programValidations, programVariables: programVariables, programRules: programRules});
+                            });
+                        });
+                    });
+                }); 
+            });                        
+            return def.promise;
         }
     };  
 })
 
-/* Returns user defined variable names and their corresponding UIDs and types for a specific program */
-.factory('TrackerRuleVariableFactory', function($rootScope, $q, TCStorageService){
-    return{
-        getProgramRuleVariables : function(programUid){
-            var def = $q.defer();
-            
-            TCStorageService.currentStore.open().done(function(){
-                
-                TCStorageService.currentStore.getAll('programRuleVariables').done(function(variables){
-                    
-                    //The array will ultimately be returned to the caller.
-                    var programRuleVariablesArray = [];
-                    //Loop through and add the variables belonging to this program
-                    angular.forEach(variables, function(variable){
-                       if(variable.program.id == programUid) {
-                            programRuleVariablesArray.push(variable);
-                       }
-                    });
-
-                    $rootScope.$apply(function(){
-                        def.resolve(programRuleVariablesArray);
-                    });
-                });
-            });
-                        
-            return def.promise;
-        }
-    };
-})
-
-/* Returns user defined variable names and their corresponding UIDs and types for a specific program */
-.factory('TrackerWidgetsConfigurationFactory', function(){
-    return{
-        getWidgetConfiguration : function(programUid){
-            //If no config exists, return default config
-            
-            return [
-                {title: 'Details', type: 'rulebound', code:"det", show: true, expand: true, horizontalplacement:"left", index:0},
-                {title: 'enrollment', type:'enrollment', show: false, expand: true, horizontalplacement:"left", index:1},
-                {title: 'dataentry', type: 'dataentry', show: true, expand: true, horizontalplacement:"left", index:2},
-                {title: 'report', type: 'report', show: false, expand: true, horizontalplacement:"left", index:3},
-                {title: 'current_selections', type: 'current_selections', show: false, expand: true, horizontalplacement:"right", index:0},
-                {title: 'profile', type: 'profile', show: false, expand: true, horizontalplacement:"right", index:1},
-                {title: 'Conditions/Complications',  type:'rulebound', code:"con", show: true, expand: true, horizontalplacement:"right", index:2},
-                {title: 'relationships', type: 'relationships', show: false, expand: true, horizontalplacement:"right", index:3},
-                {title: 'notes', type: 'notes', show: true, expand: true, horizontalplacement:"right", index:4},
-                {title: 'Summary', type: 'rulebound', code:"sum", show: true, expand: true, horizontalplacement:"left", index:4}
-            ];
-        },
-        getDefaultWidgetConfiguration: function() {
-            return [
-                {title: 'enrollment', type:'enrollment', show: true, expand: true, horizontalplacement:"left", index:0},
-                {title: 'dataentry', type: 'dataentry', show: true, expand: true, horizontalplacement:"left", index:1},
-                {title: 'report', type: 'report', show: true, expand: true, horizontalplacement:"left", index:2},
-                {title: 'current_selections', type: 'current_selections', show: false, expand: true, horizontalplacement:"right", index:0},
-                {title: 'profile', type: 'profile', show: true, expand: true, horizontalplacement:"right", index:1},
-                {title: 'relationships', type: 'relationships', show: true, expand: true, horizontalplacement:"right", index:2},
-                {title: 'notes', type: 'notes', show: true, expand: true, horizontalplacement:"right", index:3}
-            ];
-        }
-    };
-            
-})
-
 .service('EntityQueryFactory', function(OperatorFactory, DateUtils){  
     
     this.getAttributesQuery = function(attributes, enrollment){

=== 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-06-23 19:48:46 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js	2015-07-01 06:24:47 +0000
@@ -1,4 +1,6 @@
 /* Pagination service */
+/* global angular, dhis2, moment */
+
 var d2Services = angular.module('d2Services', ['ngResource'])
 
 /* Factory for loading translation strings */
@@ -712,589 +714,510 @@
 })
 
 /* service for building variables based on the data in users fields */
-.service('VariableService', function($rootScope,$q,TrackerRuleVariableFactory,DateUtils,CalendarService,MetaDataFactory,$filter,orderByFilter,$log){
-    return {
-        getVariables: function(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity, variablesFromIndicators, selectedEnrollment) {
-            var thePromisedVariables = $q.defer();
+.service('VariableService', function(DateUtils,$filter,$log){
+    
+    var pushVariable = function(variables, variablename, variableValue, variableType, variablefound, variablePrefix) {
+        //First clean away single or double quotation marks at the start and end of the variable name.
+        variableValue = $filter('trimquotes')(variableValue);
+
+        //Append single quotation marks in case the variable is of text or date type:
+        if(variableType === 'string' || variableType === 'date') {
+            variableValue = "'" + variableValue + "'";
+        }
+        else if(variableType === 'bool' || variableType === 'trueOnly') {
+            if(eval(variableValue)) {
+                variableValue = true;
+            }
+            else {
+                variableValue = false;
+            }
+        }
+        else if(variableType === "int" || variableType === "number") {
+            variableValue = Number(variableValue);
+        }
+        else{
+            $log.warn("unknown datatype:" + variableType);
+        }
+
+        variables[variablename] = {
+                        variableValue:variableValue,
+                        variableType:variableType,
+                        hasValue:variablefound,
+                        variablePrefix:variablePrefix
+                    };
+        return variables;            
+    };
+    
+    return {        
+        processVariables: function(variables, variablename, variableValue, variableType, variablefound, variablePrefix) {            
+            return pushVariable(variables, variablename, variableValue, variableType, variablefound, variablePrefix);
+        },
+        getVariables: function(allProgramRules, executingEvent, evs, allDes, selectedEntity, selectedEnrollment) {
             var variables = {};
             
-            var pushVariable = function(variablename, variableValue, variableType, variablefound, variablePrefix) {
-                //First clean away single or double quotation marks at the start and end of the variable name.
-                variableValue = $filter('trimquotes')(variableValue);
-                
-                //Append single quotation marks in case the variable is of text type:
-                if(variableType === 'string') {
-                    variableValue = "'" + variableValue + "'";
-                }
-                else if(variableType === 'date') {
-                    variableValue = "'" + variableValue + "'";
-                }
-                else if(variableType === 'bool' || variableType === 'trueOnly') {
-                    if(eval(variableValue)) {
-                        variableValue = true;
+            var programVariables = allProgramRules.programVariables;            
+                            
+            programVariables = programVariables.concat(allProgramRules.programIndicators.variables);
+
+            angular.forEach(programVariables, function(programVariable) {
+                var dataElementId = programVariable.dataElement;
+                if(programVariable.dataElement && programVariable.dataElement.id) {
+                    dataElementId = programVariable.dataElement.id;
+                }
+
+                var programStageId = programVariable.programStage;
+                if(programVariable.programStage && programVariable.programStage.id) {
+                    programStageId = programVariable.programStage.id;
+                }
+
+                var valueFound = false;
+                if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE"){
+                    if(programStageId) {
+                        angular.forEach(evs.byStage[programStageId], function(event) {
+                            if(angular.isDefined(event[dataElementId])
+                                    && event[dataElementId] !== null ){
+                                valueFound = true;
+                                variables = pushVariable(variables, programVariable.name, event[dataElementId], allDes[dataElementId].dataElement.type, valueFound, '#');
+                            }
+                        });
+                    } else {
+                        $log.warn("Variable id:'" + programVariable.id + "' name:'" + programVariable.name 
+                                + "' does not have a programstage defined,"
+                                + " despite that the variable has sourcetype DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE" );
+                    }
+
+                }
+                else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM"){
+                    angular.forEach(evs.all, function(event) {
+                        if(angular.isDefined(event[dataElementId])
+                                && event[dataElementId] !== null ){
+                            valueFound = true;
+                            variables = pushVariable(variables, programVariable.name, event[dataElementId], allDes[dataElementId].dataElement.type, valueFound, '#' );
+                         }
+                    });
+                }
+                else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_CURRENT_EVENT"){
+                    if(angular.isDefined(executingEvent[dataElementId])
+                            && executingEvent[dataElementId] !== null ){
+                        valueFound = true;
+                        variables = pushVariable(variables, programVariable.name, executingEvent[dataElementId], allDes[dataElementId].dataElement.type, valueFound, '#' );
+                    }      
+                }
+                else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_PREVIOUS_EVENT"){
+                    //Only continue checking for a value if there is more than one event.
+                    if(evs.all && evs.all.length > 1) {
+                        var previousvalue = null;
+                        var currentEventPassed = false;
+                        for(var i = 0; i < evs.all.length; i++) {
+                            //Store the values as we iterate through the stages
+                            //If the event[i] is not the current event, it is older(previous). Store the previous value if it exists
+                            if(!currentEventPassed && evs.all[i] !== executingEvent && 
+                                    angular.isDefined(evs.all[i][dataElementId])) {
+                                previousvalue = evs.all[i][dataElementId];
+                                valueFound = true;
+                            }
+                            else if(evs.all[i] === executingEvent) {
+                                //We have iterated to the newest event - store the last collected variable value - if any is found:
+                                if(valueFound) {
+                                    variables = pushVariable(variables, programVariable.name, previousvalue, allDes[dataElementId].dataElement.type, valueFound, '#' );
+                                }
+                                //Set currentEventPassed, ending the iteration:
+                                currentEventPassed = true;
+                            }
+                        }
+                    }
+                }
+                else if(programVariable.programRuleVariableSourceType === "TEI_ATTRIBUTE"){
+                    angular.forEach(selectedEntity.attributes , function(attribute) {
+                        if(!valueFound) {
+                            if(attribute.attribute === programVariable.trackedEntityAttribute.id) {
+                                valueFound = true;
+                                variables = pushVariable(variables, programVariable.name, attribute.value, attribute.type, valueFound, '#' );
+                            }
+                        }
+                    });
+                }
+                else if(programVariable.programRuleVariableSourceType === "CALCULATED_VALUE"){
+                    //We won't assign the calculated variables at this step. The rules execution will calculate and assign the variable.
+                }
+                else if(programVariable.programRuleVariableSourceType === "NUMBEROFEVENTS_PROGRAMSTAGE"){
+                    var numberOfEvents = 0;
+                    if( programStageId && evs.byStage[programStageId] ) {
+                        numberOfEvents = evs.byStage[programStageId].length;
+                    }
+                    valueFound = true;
+                    variables = pushVariable(variables, programVariable.name, numberOfEvents, 'int', valueFound, '#' );
+                }
+                else {
+                    //Missing handing of ruletype
+                    $log.warn("Unknown programRuleVariableSourceType:" + programVariable.programRuleVariableSourceType);
+                }
+
+
+                if(!valueFound){
+                    //If there is still no value found, assign default value:
+                    if(dataElementId) {
+                        var dataElement = allDes[dataElementId];
+                        if( dataElement ) {
+                            variables = pushVariable(variables, programVariable.name, "", dataElement.dataElement.type, false, '#' );
+                        } 
+                        else {
+                            $log.warn("Variable #{" + programVariable.name + "} is linked to a dataelement that is not part of the program");
+                            variables = pushVariable(variables, programVariable.name, "", "string",false, '#' );
+                        }
                     }
                     else {
-                        variableValue = false;
-                    }
-                }
-                else if(variableType === "int" || variableType === "number") {
-                    variableValue = Number(variableValue);
-                }
-                else{
-                    $log.warn("unknown datatype:" + variableType);
-                }
-
-                variables[variablename] = {
-                                variableValue:variableValue,
-                                variableType:variableType,
-                                hasValue:variablefound,
-                                variablePrefix:variablePrefix
-                            };
-            };
-            MetaDataFactory.getAll('constants').then(function(constants) {
-                TrackerRuleVariableFactory.getProgramRuleVariables(programid).then(function(programVariables){
-
-                    programVariables = programVariables.concat(variablesFromIndicators);
-
-                    var allEventsSorted = [];
-                    var currentEvent = executingEvent;
-                    var eventsSortedPerProgramStage = [];
-
-                    for(var key in allEventsByStage){
-                        if(allEventsByStage.hasOwnProperty(key)){
-                            eventsSortedPerProgramStage[key] = [];
-                            angular.forEach(allEventsByStage[key], function(event){
-                                allEventsSorted.push(event);
-                                eventsSortedPerProgramStage[key].push(event);
-                            });
-                            eventsSortedPerProgramStage[key] = orderByFilter(eventsSortedPerProgramStage[key], '-sortingDate').reverse(); 
-                        }
-                    }
-                    allEventsSorted = orderByFilter(allEventsSorted, '-sortingDate').reverse(); 
-
-                    var allDes = allDataElements;
-
-                    angular.forEach(programVariables, function(programVariable) {
-                        var dataElementId = programVariable.dataElement;
-                        if(programVariable.dataElement.id) {
-                            dataElementId = programVariable.dataElement.id;
-                        }
-
-                        var programStageId = programVariable.programStage;
-                        if(programVariable.programStage.id) {
-                            programStageId = programVariable.programStage.id;
-                        }
-
-                        var valueFound = false;
-                        if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE"){
-                            if(programVariable.programStage) {
-                                angular.forEach(eventsSortedPerProgramStage[programStageId], function(event) {
-                                    if(angular.isDefined(event[dataElementId])
-                                            && event[dataElementId] !== null ){
-                                        valueFound = true;
-                                        pushVariable(programVariable.name, event[dataElementId], allDes[dataElementId].dataElement.type, valueFound, '#');
-                                    }
-                                });
-                            } else {
-                                $log.warn("Variable id:'" + programVariable.id + "' name:'" + programVariable.name 
-                                        + "' does not have a programstage defined,"
-                                        + " despite that the variable has sourcetype DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE" );
-                            }
-
-                        }
-                        else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_NEWEST_EVENT_PROGRAM"){
-                            angular.forEach(allEventsSorted, function(event) {
-                                if(angular.isDefined(event[dataElementId])
-                                        && event[dataElementId] !== null ){
-                                    valueFound = true;
-                                     pushVariable(programVariable.name, event[dataElementId], allDes[dataElementId].dataElement.type, valueFound, '#' );
-                                 }
-                            });
-                        }
-                        else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_CURRENT_EVENT"){
-                            if(angular.isDefined(currentEvent[dataElementId])
-                                    && currentEvent[dataElementId] !== null ){
-                                valueFound = true;
-                                pushVariable(programVariable.name, currentEvent[dataElementId], allDes[dataElementId].dataElement.type, valueFound, '#' );
-                            }      
-                        }
-                        else if(programVariable.programRuleVariableSourceType === "DATAELEMENT_PREVIOUS_EVENT"){
-                            //Only continue checking for a value if there is more than one event.
-                            if(allEventsSorted && allEventsSorted.length > 1) {
-                                var previousvalue = null;
-                                var currentEventPassed = false;
-                                for(var i = 0; i < allEventsSorted.length; i++) {
-                                    //Store the values as we iterate through the stages
-                                    //If the event[i] is not the current event, it is older(previous). Store the previous value if it exists
-                                    if(!currentEventPassed && allEventsSorted[i] !== currentEvent && 
-                                            angular.isDefined(allEventsSorted[i][dataElementId])) {
-                                        previousvalue = allEventsSorted[i][dataElementId];
-                                        valueFound = true;
-                                    }
-                                    else if(allEventsSorted[i] === currentEvent) {
-                                        //We have iterated to the newest event - store the last collected variable value - if any is found:
-                                        if(valueFound) {
-                                            pushVariable(programVariable.name, previousvalue, allDes[dataElementId].dataElement.type, valueFound, '#' );
-                                        }
-                                        //Set currentEventPassed, ending the iteration:
-                                        currentEventPassed = true;
-                                    }
-                                }
-                            }
-                        }
-                        else if(programVariable.programRuleVariableSourceType === "TEI_ATTRIBUTE"){
-                            angular.forEach(selectedEntity.attributes , function(attribute) {
-                                if(!valueFound) {
-                                    if(attribute.attribute === programVariable.trackedEntityAttribute.id) {
-                                        valueFound = true;
-                                        pushVariable(programVariable.name, attribute.value, attribute.type, valueFound, '#' );
-                                    }
-                                }
-                            });
-                        }
-                        else if(programVariable.programRuleVariableSourceType === "CALCULATED_VALUE"){
-                            //We won't assign the calculated variables at this step. The rules execution will calculate and assign the variable.
-                        }
-                        else if(programVariable.programRuleVariableSourceType === "NUMBEROFEVENTS_PROGRAMSTAGE"){
-                            var numberOfEvents = 0;
-                            if( programVariable.programStage && eventsSortedPerProgramStage[programStageId] ) {
-                                numberOfEvents = eventsSortedPerProgramStage[programStageId].length;
-                            }
-                            valueFound = true;
-                            pushVariable(programVariable.name, numberOfEvents, 'int', valueFound, '#' );
-                        }
-                        else {
-                            //Missing handing of ruletype
-                            $log.warn("Unknown programRuleVariableSourceType:" + programVariable.programRuleVariableSourceType);
-                        }
-
-
-                        if(!valueFound){
-                            //If there is still no value found, assign default value:
-                            if(programVariable.dataElement) {
-                                var dataElement = allDes[dhis2.tc];
-                                if( dataElement ) {
-                                    pushVariable(programVariable.name, "", dataElement.dataElement.type, false, '#' );
-                                } 
-                                else {
-                                    $log.warn("Variable #{" + programVariable.name + "} is linked to a dataelement that is not part of the program");
-                                    pushVariable(programVariable.name, "", "string",false, '#' );
-                                }
-                            }
-                            else {
-                                pushVariable(programVariable.name, "", "string",false, '#' );
-                            }
-                        }
-                    });
-
-                    //add context variables:
-                    //last parameter "valuefound" is always true for event date
-                    pushVariable('incident_date', executingEvent.eventDate, 'date', true, 'V' );
-                    pushVariable('current_date', DateUtils.getToday(), 'date', true, 'V' );
-                    if(selectedEnrollment){
-                        pushVariable('enrollment_date', selectedEnrollment.dateOfEnrollment, 'date', true, 'V' );
-                    }
-                    
-                    //pushVariable('value_count', executingEvent.eventDate, 'date', true, 'V' );
-                    //pushVariable('zero_pos_value_count', executingEvent.eventDate, 'date', true, 'V' );
-
-                    //Push all constant values:
-                    angular.forEach(constants, function(constant){
-                        pushVariable(constant.id, constant.value, 'int', true, 'C' );
-                    });
-
-                    thePromisedVariables.resolve(variables);
-                }); 
-            });
-            
-            return thePromisedVariables.promise;
+                        variables = pushVariable(variables, programVariable.name, "", "string",false, '#' );
+                    }
+                }
+            });
+
+            //add context variables:
+            //last parameter "valuefound" is always true for event date
+            variables = pushVariable(variables, 'incident_date', executingEvent.eventDate, 'date', true, 'V' );
+            variables = pushVariable(variables, 'current_date', DateUtils.getToday(), 'date', true, 'V' );
+            if(selectedEnrollment){
+                variables = pushVariable(variables, 'enrollment_date', selectedEnrollment.dateOfEnrollment, 'date', true, 'V' );
+            }
+
+            //variables = pushVariable(variables, 'value_count', executingEvent.eventDate, 'date', true, 'V' );
+            //variables = pushVariable(variables, 'zero_pos_value_count', executingEvent.eventDate, 'date', true, 'V' );
+
+            //Push all constant values:
+            angular.forEach(allProgramRules.constants, function(constant){
+                variables = pushVariable(variables, constant.id, constant.value, 'int', true, 'C' );
+            });
+
+            return variables;
         }
     };
 })
 
 /* service for executing tracker rules and broadcasting results */
-.service('TrackerRulesExecutionService', function(TrackerRulesFactory, MetaDataFactory, VariableService, $rootScope, $log, $q, $filter, orderByFilter){
+.service('TrackerRulesExecutionService', function(VariableService, $rootScope, $log, $filter, orderByFilter){
+    
+    var replaceVariables = function(expression, variablesHash){
+        //replaces the variables in an expression with actual variable values.                
+
+        //Check if the expression contains variables at all(any dollar signs):
+        if(expression.indexOf('#{') !== -1) {
+            //Find every variable name in the expression;
+            var variablespresent = expression.match(/#{\w+.?\w*}/g);
+            //Replace each matched variable:
+            angular.forEach(variablespresent, function(variablepresent) {
+                //First strip away any prefix and postfix signs from the variable name:
+                variablepresent = variablepresent.replace("#{","").replace("}","");
+
+                if(angular.isDefined(variablesHash[variablepresent]) &&
+                        variablesHash[variablepresent].variablePrefix === '#') {
+                    //Replace all occurrences of the variable name(hence using regex replacement):
+                    expression = expression.replace(new RegExp("#{" + variablepresent + "}", 'g'),
+                        variablesHash[variablepresent].variableValue);
+                }
+                else {
+                    $log.warn("Expression " + expression + " conains variable " + variablepresent 
+                            + " - but this variable is not defined." );
+                }  
+            });
+        }
+
+        //Check if the expression contains program variables
+        if(expression.indexOf('V{') !== -1) {
+            //Find every variable name in the expression;
+            var variablespresent = expression.match(/V{\w+.?\w*}/g);
+            //Replace each matched variable:
+            angular.forEach(variablespresent, function(variablepresent) {
+                //First strip away any prefix and postfix signs from the variable name:
+                variablepresent = variablepresent.replace("V{","").replace("}","");
+
+                if(angular.isDefined(variablesHash[variablepresent]) &&
+                        variablesHash[variablepresent].variablePrefix === 'V') {
+                    //Replace all occurrences of the variable name(hence using regex replacement):
+                    expression = expression.replace(new RegExp("V{" + variablepresent + "}", 'g'),
+                        variablesHash[variablepresent].variableValue);
+                }
+                else {
+                    $log.warn("Expression " + expression + " conains context variable " + variablepresent 
+                            + " - but this variable is not defined." );
+                } 
+            });
+        }
+
+        //Check if the expression contains constants
+        if(expression.indexOf('C{') !== -1) {
+            //Find every constant in the expression;
+            var variablespresent = expression.match(/C{\w+.?\w*}/g);
+            //Replace each matched variable:
+            angular.forEach(variablespresent, function(variablepresent) {
+                //First strip away any prefix and postfix signs from the variable name:
+                variablepresent = variablepresent.replace("C{","").replace("}","");
+
+                if(angular.isDefined(variablesHash[variablepresent]) &&
+                        variablesHash[variablepresent].variablePrefix === 'C') {
+                    //Replace all occurrences of the variable name(hence using regex replacement):
+                    expression = expression.replace(new RegExp("C{" + variablepresent + "}", 'g'),
+                        variablesHash[variablepresent].variableValue);
+                }
+                else {
+                    $log.warn("Expression " + expression + " conains constant " + variablepresent 
+                            + " - but this constant is not defined." );
+                } 
+            });
+        }
+        
+        return expression;            
+    };
+    
+    var runDhisFunctions = function(expression, variablesHash, flag){
+        //Called from "runExpression". Only proceed with this logic in case there seems to be dhis function calls: "dhis." is present.
+        if(angular.isDefined(expression) && expression.indexOf("dhis.") !== -1){   
+            var dhisFunctions = [{name:"dhis.daysbetween",parameters:2},
+                                {name:"dhis.floor",parameters:1},
+                                {name:"dhis.modulus",parameters:2},
+                                {name:"dhis.hasValue",parameters:1},
+                                {name:"dhis.concatenate"}];
+
+            angular.forEach(dhisFunctions, function(dhisFunction){
+                //Replace each * with a regex that matches each parameter, allowing commas only inside single quotation marks.
+                var regularExFunctionCall = new RegExp(dhisFunction.name.replace(".","\\.") + "\\([^\\)]*\\)",'g');
+                var callsToThisFunction = expression.match(regularExFunctionCall);
+                angular.forEach(callsToThisFunction, function(callToThisFunction){
+                    //Remove the function name and paranthesis:
+                    var justparameters = callToThisFunction.replace(/(^[^\(]+\()|\)$/g,"");
+                    //Then split into single parameters:
+                    var parameters = justparameters.match(/(('[^']+')|([^,]+))/g);
+
+                    //Show error if no parameters is given and the function requires parameters,
+                    //or if the number of parameters is wrong.
+                    if(angular.isDefined(dhisFunction.parameters)){
+                        //But we are only checking parameters where the dhisFunction actually has a defined set of parameters(concatenate, for example, does not have a fixed number);
+                        if((!angular.isDefined(parameters) && dhisFunction.parameters > 0)
+                                || parameters.length !== dhisFunction.parameters){
+                            $log.warn(dhisFunction.name + " was called with the incorrect number of parameters");
+                        }
+                    }
+
+                    //In case the function call is nested, the parameter itself contains an expression, run the expression.
+                    if(angular.isDefined(parameters)) {
+                        for (var i = 0; i < parameters.length; i++) {
+                            parameters[i] = runExpression(parameters[i],dhisFunction.name,"parameter:" + i, flag, variablesHash);
+                        }
+                    }
+
+                    //Special block for dhis.weeksBetween(*,*) - add such a block for all other dhis functions.
+                    if(dhisFunction.name === "dhis.daysbetween")
+                    {
+                        var firstdate = $filter('trimquotes')(parameters[0]);
+                        var seconddate = $filter('trimquotes')(parameters[1]);
+                        firstdate = moment(firstdate);
+                        seconddate = moment(seconddate);
+                        //Replace the end evaluation of the dhis function:
+                        expression = expression.replace(callToThisFunction, seconddate.diff(firstdate,'days'));
+                    }
+                    else if(dhisFunction.name === "dhis.floor")
+                    {
+                        var floored = Math.floor(parameters[0]);
+                        //Replace the end evaluation of the dhis function:
+                        expression = expression.replace(callToThisFunction, floored);
+                    }
+                    else if(dhisFunction.name === "dhis.modulus")
+                    {
+                        var dividend = Number(parameters[0]);
+                        var divisor = Number(parameters[1]);
+                        var rest = dividend % divisor;
+                        //Replace the end evaluation of the dhis function:
+                        expression = expression.replace(callToThisFunction, rest);
+                    }
+                    else if(dhisFunction.name === "dhis.hasValue")
+                    {
+                        //"evaluate" hasvalue to true or false:
+                        if(variablesHash[parameters[0]].hasValue){
+                            expression = expression.replace(callToThisFunction, 'true');
+                        } else {
+                            expression = expression.replace(callToThisFunction, 'false');
+                        }
+                    }
+                    else if(dhisFunction.name === "dhis.concatenate")
+                    {
+                        var returnString = "'";
+                        for (var i = 0; i < parameters.length; i++) {
+                            returnString += parameters[i];
+                        }
+                        returnString += "'";
+                        expression = expression.replace(callToThisFunction, returnString);
+                    }
+                });
+            });
+        }
+
+        return expression;
+    };
+    
+    var runExpression = function(expression, beforereplacement, identifier, flag, variablesHash ){
+        //determine if expression is true, and actions should be effectuated
+        //If DEBUG mode, use try catch and report errors. If not, omit the heavy try-catch loop.:
+        var answer = false;
+        if(flag.debug) {
+            try{
+
+                var dhisfunctionsevaluated = runDhisFunctions(expression, variablesHash, flag);
+                answer = eval(dhisfunctionsevaluated);
+
+                if(flag.verbose)
+                {
+                    $log.info("Expression with id " + identifier + " was successfully run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - Result of evaluation was:" + answer);
+                }
+            }
+            catch(e)
+            {
+                $log.warn("Expression with id " + identifier + " could not be run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - error message:" + e);
+            }
+        }
+        else {
+            //Just run the expression. This is much faster than the debug route: http://jsperf.com/try-catch-block-loop-performance-comparison
+            var dhisfunctionsevaluated = runDhisFunctions(expression, variablesHash, flag);
+            answer = eval(dhisfunctionsevaluated);
+        }
+        return answer;
+    }; 
+    
     return {
-        executeRules: function(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity, selectedEnrollment, verbose ) {
-            //When debugging rules, the caller should provide a variable for wether or not the rules is being debugged.
-            //hard coding this for now:
-            var debug = true;
-            
-            var variablesHash = {};
-            
-            var replaceVariables = function(expression) {
-                //replaces the variables in an expression with actual variable values.
-                //First check if the expression contains variables at all(any dollar signs):
-                if(expression.indexOf('#{') !== -1) {
-                    //Find every variable name in the expression;
-                    var variablespresent = expression.match(/#{\w+.?\w*}/g);
-                    //Replace each matched variable:
-                    angular.forEach(variablespresent, function(variablepresent) {
-                        //First strip away any prefix and postfix signs from the variable name:
-                        variablepresent = variablepresent.replace("#{","").replace("}","");
-                        
-                        if(angular.isDefined(variablesHash[variablepresent]) &&
-                                variablesHash[variablepresent].variablePrefix === '#') {
-                            //Replace all occurrences of the variable name(hence using regex replacement):
-                            expression = expression.replace(new RegExp("#{" + variablepresent + "}", 'g'),
-                                variablesHash[variablepresent].variableValue);
-                        }
-                        else {
-                            $log.warn("Expression " + expression + " conains variable " + variablepresent 
-                                    + " - but this variable is not defined." );
-                        }  
-                    });
-                }
-                
-                if(expression.indexOf('V{') !== -1) {
-                    //Find every variable name in the expression;
-                    var variablespresent = expression.match(/V{\w+.?\w*}/g);
-                    //Replace each matched variable:
-                    angular.forEach(variablespresent, function(variablepresent) {
-                        //First strip away any prefix and postfix signs from the variable name:
-                        variablepresent = variablepresent.replace("V{","").replace("}","");
-                        
-                        if(angular.isDefined(variablesHash[variablepresent]) &&
-                                variablesHash[variablepresent].variablePrefix === 'V') {
-                            //Replace all occurrences of the variable name(hence using regex replacement):
-                            expression = expression.replace(new RegExp("V{" + variablepresent + "}", 'g'),
-                                variablesHash[variablepresent].variableValue);
-                        }
-                        else {
-                            $log.warn("Expression " + expression + " conains context variable " + variablepresent 
-                                    + " - but this variable is not defined." );
-                        } 
-                    });
-                }
-                
-                if(expression.indexOf('C{') !== -1) {
-                    //Find every constant in the expression;
-                    var variablespresent = expression.match(/C{\w+.?\w*}/g);
-                    //Replace each matched variable:
-                    angular.forEach(variablespresent, function(variablepresent) {
-                        //First strip away any prefix and postfix signs from the variable name:
-                        variablepresent = variablepresent.replace("C{","").replace("}","");
-                        
-                        if(angular.isDefined(variablesHash[variablepresent]) &&
-                                variablesHash[variablepresent].variablePrefix === 'C') {
-                            //Replace all occurrences of the variable name(hence using regex replacement):
-                            expression = expression.replace(new RegExp("C{" + variablepresent + "}", 'g'),
-                                variablesHash[variablepresent].variableValue);
-                        }
-                        else {
-                            $log.warn("Expression " + expression + " conains constant " + variablepresent 
-                                    + " - but this constant is not defined." );
-                        } 
-                    });
-                }
-                return expression;
-            };
-            
-            var runDhisFunctions = function(expression) {
-                //Called from "runExpression". Only proceed with this logic in case there seems to be dhis function calls: "dhis." is present.
-                if(angular.isDefined(expression) && expression.indexOf("dhis.") !== -1){   
-                    var dhisFunctions = [{name:"dhis.daysbetween",parameters:2},
-                                        {name:"dhis.floor",parameters:1},
-                                        {name:"dhis.modulus",parameters:2},
-                                        {name:"dhis.hasValue",parameters:1},
-                                        {name:"dhis.concatenate"}];
-                    
-                    angular.forEach(dhisFunctions, function(dhisFunction){
-                        //Replace each * with a regex that matches each parameter, allowing commas only inside single quotation marks.
-                        var regularExFunctionCall = new RegExp(dhisFunction.name.replace(".","\\.") + "\\([^\\)]*\\)",'g');
-                        var callsToThisFunction = expression.match(regularExFunctionCall);
-                        angular.forEach(callsToThisFunction, function(callToThisFunction){
-                            //Remove the function name and paranthesis:
-                            var justparameters = callToThisFunction.replace(/(^[^\(]+\()|\)$/g,"");
-                            //Then split into single parameters:
-                            var parameters = justparameters.match(/(('[^']+')|([^,]+))/g);
-                            
-                            //Show error if no parameters is given and the function requires parameters,
-                            //or if the number of parameters is wrong.
-                            if(angular.isDefined(dhisFunction.parameters)){
-                                //But we are only checking parameters where the dhisFunction actually has a defined set of parameters(concatenate, for example, does not have a fixed number);
-                                if((!angular.isDefined(parameters) && dhisFunction.parameters > 0)
-                                        || parameters.length !== dhisFunction.parameters){
-                                    $log.warn(dhisFunction.name + " was called with the incorrect number of parameters");
-                                }
-                            }
-
-                            //In case the function call is nested, the parameter itself contains an expression, run the expression.
-                            if(angular.isDefined(parameters)) {
-                                for (var i = 0; i < parameters.length; i++) {
-                                    parameters[i] = runExpression(parameters[i],dhisFunction.name,"parameter:" + i);
-                                }
-                            }
-
-                            //Special block for dhis.weeksBetween(*,*) - add such a block for all other dhis functions.
-                            if(dhisFunction.name === "dhis.daysbetween")
-                            {
-                                var firstdate = $filter('trimquotes')(parameters[0]);
-                                var seconddate = $filter('trimquotes')(parameters[1]);
-                                firstdate = moment(firstdate);
-                                seconddate = moment(seconddate);
-                                //Replace the end evaluation of the dhis function:
-                                expression = expression.replace(callToThisFunction, seconddate.diff(firstdate,'days'));
-                            }
-                            else if(dhisFunction.name === "dhis.floor")
-                            {
-                                var floored = Math.floor(parameters[0]);
-                                //Replace the end evaluation of the dhis function:
-                                expression = expression.replace(callToThisFunction, floored);
-                            }
-                            else if(dhisFunction.name === "dhis.modulus")
-                            {
-                                var dividend = Number(parameters[0]);
-                                var divisor = Number(parameters[1]);
-                                var rest = dividend % divisor;
-                                //Replace the end evaluation of the dhis function:
-                                expression = expression.replace(callToThisFunction, rest);
-                            }
-                            else if(dhisFunction.name === "dhis.hasValue")
-                            {
-                                //"evaluate" hasvalue to true or false:
-                                if(variablesHash[parameters[0]].hasValue){
-                                    expression = expression.replace(callToThisFunction, 'true');
-                                } else {
-                                    expression = expression.replace(callToThisFunction, 'false');
-                                }
-                            }
-                            else if(dhisFunction.name === "dhis.concatenate")
-                            {
-                                var returnString = "'";
-                                for (var i = 0; i < parameters.length; i++) {
-                                    returnString += parameters[i];
-                                }
-                                returnString += "'";
-                                expression = expression.replace(callToThisFunction, returnString);
-                            }
-                        });
-                    });
-                }
-                
-                return expression;
-            };
-            
-            var runExpression = function(expression, beforereplacement, identifier ){
-                //determine if expression is true, and actions should be effectuated
-                //If DEBUG mode, use try catch and report errors. If not, omit the heavy try-catch loop.:
-                var answer = false;
-                if(debug) {
-                    try{
-                        
-                        var dhisfunctionsevaluated = runDhisFunctions(expression);
-                        answer = eval(dhisfunctionsevaluated);
-
-                        if(verbose)
-                        {
-                            $log.info("Expression with id " + identifier + " was successfully run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - Result of evaluation was:" + answer);
-                        }
-                    }
-                    catch(e)
-                    {
-                        $log.warn("Expression with id " + identifier + " could not be run. Original condition was: " + beforereplacement + " - Evaluation ended up as:" + expression + " - error message:" + e);
-                    }
-                }
-                else {
-                    //Just run the expression. This is much faster than the debug route: http://jsperf.com/try-catch-block-loop-performance-comparison
-                    var dhisfunctionsevaluated = runDhisFunctions(expression);
-                    answer = eval(dhisfunctionsevaluated);
-                }
-                return answer;
-            };
-            
-        var getIndicatorRules = function (programid) {
-                var def = $q.defer();
-                MetaDataFactory.getByProgram('programIndicators', programid).then(function(pis){
-                    var variables = [];
-                    
-                    var programRules = [];
-                    
-                    angular.forEach(pis, function(pi){
-                        var newAction = {
-                                id:pi.id,
-                                content:pi.displayDescription,
-                                data:pi.expression,
-                                programRuleActionType:'DISPLAYKEYVALUEPAIR',
-                                location:'indicators'
-                            };
-                        var newRule = {
-                                name:pi.name,
-                                shortname:pi.shortname,
-                                code:pi.code,
-                                program:pi.program,
-                                description:pi.description,
-                                condition:pi.filter ? pi.filter : 'true',
-                                programRuleActions: [newAction]
-                            };
-                            
-                        programRules.push(newRule);
-                        
-                        var variablesInCondition = newRule.condition.match(/#{\w+.?\w*}/g);
-                        var variablesInData = newAction.data.match(/#{\w+.?\w*}/g);
-                        
-                        var pushDirectAddressedVariable = function(variableWithCurls) {
-                            var variableName = variableWithCurls.replace("#{","").replace("}","");
-                            var variableNameParts = variableName.split('.');
-                            
-                           
-                            if(variableNameParts.length === 2) {
-                                //this is a programstage and dataelement specification. translate to program variable:
-                                variables.push({
-                                    name:variableName,
-                                    programRuleVariableSourceType:'DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE',
-                                    dataElement:variableNameParts[1],
-                                    programStage:variableNameParts[0],
-                                    program:programid
-                                });
-                            }
-                            else if(variableNameParts.length === 1)
-                            {
-                                //This is an attribute - let us translate to program variable:
-                                variables.push({
-                                    name:variableName,
-                                    programRuleVariableSourceType:'TEI_ATTRIBUTE',
-                                    trackedEntityAttribute:variableNameParts[0],
-                                    program:programid
-                                });
-                            }
-                         
-                        };
-                        
-                        angular.forEach(variablesInCondition, function(variableInCondition) {
-                            pushDirectAddressedVariable(variableInCondition);
-                        });
-                        
-                        angular.forEach(variablesInData, function(variableInData) {
-                            pushDirectAddressedVariable(variableInData);
-                        });
-                    });
-
-                    def.resolve({rules:programRules,variables:variables});
-                });
-                return def.promise;       
-            };
-            
-            
-            getIndicatorRules(programid).then(function(indicatorRulesAndVariables){
-                VariableService.getVariables(programid, executingEvent, allEventsByStage, allDataElements, selectedEntity,indicatorRulesAndVariables.variables, selectedEnrollment).then(function(variablesReceived){
-                    TrackerRulesFactory.getProgramStageRules(programid, executingEvent.programStage).then(function(rules){
-                        //Concatenate rules produced by indicator definitions into the other rules:
-                        rules = rules.concat(indicatorRulesAndVariables.rules);
-                        
-                        //Run rules in priority - lowest number first(priority null is last)
-                        rules = orderByFilter(rules, 'priority');
-
-                        variablesHash = variablesReceived;
-
-                        if(angular.isObject(rules) && angular.isArray(rules)){
-                            //The program has rules, and we want to run them.
-                            //Prepare repository unless it is already prepared:
-                            if(angular.isUndefined( $rootScope.ruleeffects ) ) {
-                                $rootScope.ruleeffects = {};
-                            }
-
-                            if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event] )){
-                                $rootScope.ruleeffects[executingEvent.event] = {};
-                            }
-
-                            var updatedEffectsExits = false;
-
-                            angular.forEach(rules, function(rule) {
-                                var ruleEffective = false;
-
-                                var expression = rule.condition;
-                                //Go through and populate variables with actual values, but only if there actually is any replacements to be made(one or more "$" is present)
-                                if(expression) {
-                                    if(expression.indexOf('{') !== -1) {
-                                        expression = replaceVariables(expression);
-                                    }
-                                    //run expression:
-                                    ruleEffective = runExpression(expression, rule.condition, "rule:" + rule.id);
-                                } else {
-                                    $log.warn("Rule id:'" + rule.id + "'' and name:'" + rule.name + "' had no condition specified. Please check rule configuration.");
-                                }
-
-                                angular.forEach(rule.programRuleActions, function(action){
-                                    //In case the effect-hash is not populated, add entries
-                                    if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event][action.id] )){
-                                        $rootScope.ruleeffects[executingEvent.event][action.id] =  {
-                                            id:action.id,
-                                            location:action.location, 
-                                            action:action.programRuleActionType,
-                                            dataElement:action.dataElement,
-                                            content:action.content,
-                                            data:action.data,
-                                            ineffect:undefined
-                                        };
-                                    }
-
-                                    //In case the rule is effective and contains specific data, 
-                                    //the effect be refreshed from the variables list.
-                                    //If the rule is not effective we can skip this step
-                                    if(ruleEffective && action.data)
-                                    {
-                                        //The key data might be containing a dollar sign denoting that the key data is a variable.
-                                        //To make a lookup in variables hash, we must make a lookup without the dollar sign in the variable name
-                                        //The first strategy is to make a direct lookup. In case the "data" expression is more complex, we have to do more replacement and evaluation.
-
-                                        var nameWithoutBrackets = action.data.replace('#{','').replace('}','');
-                                        if(angular.isDefined(variablesHash[nameWithoutBrackets]))
-                                        {
-                                            //The variable exists, and is replaced with its corresponding value
-                                            $rootScope.ruleeffects[executingEvent.event][action.id].data =
-                                                variablesHash[nameWithoutBrackets].variableValue;
-                                        }
-                                        else if(action.data.indexOf('{') !== -1)
-                                        {
-                                            //Since the value couldnt be looked up directly, and contains a dollar sign, the expression was more complex
-                                            //Now we will have to make a thorough replacement and separate evaluation to find the correct value:
-                                            $rootScope.ruleeffects[executingEvent.event][action.id].data = replaceVariables(action.data);
-                                            //In a scenario where the data contains a complex expression, evaluate the expression to compile(calculate) the result:
-                                            $rootScope.ruleeffects[executingEvent.event][action.id].data = runExpression($rootScope.ruleeffects[executingEvent.event][action.id].data, action.data, "action:" + action.id);
-                                        }
-                                    }
-
-                                    //Update the rule effectiveness if it changed in this evaluation;
-                                    if($rootScope.ruleeffects[executingEvent.event][action.id].ineffect !== ruleEffective)
-                                    {
-                                        //There is a change in the rule outcome, we need to update the effect object.
-                                        updatedEffectsExits = true;
-                                        $rootScope.ruleeffects[executingEvent.event][action.id].ineffect = ruleEffective;
-                                    }
-
-                                    //In case the rule is of type "assign variable" and the rule is effective,
-                                    //the variable data result needs to be applied to the correct variable:
-                                    if($rootScope.ruleeffects[executingEvent.event][action.id].action === "ASSIGNVARIABLE" && $rootScope.ruleeffects[executingEvent.event][action.id].ineffect){
-                                        //from earlier evaluation, the data portion of the ruleeffect now contains the value of the variable to be assign.
-                                        //the content portion of the ruleeffect defines the name for the variable, when dollar is removed:
-                                        var variabletoassign = $rootScope.ruleeffects[executingEvent.event][action.id].content.replace("#{","").replace("}","");
-
-                                        if(!angular.isDefined(variablesHash[variabletoassign])){
-                                            $log.warn("Variable " + variabletoassign + " was not defined.");
-                                        }
-
-                                        //Even if the variable is not defined: we assign it:
-                                        if(variablesHash[variabletoassign].variableValue !== $rootScope.ruleeffects[executingEvent.event][action.id].data){
-                                            //If the variable was actually updated, we assume that there is an updated ruleeffect somewhere:
-                                            updatedEffectsExits = true;
-                                            //Then we assign the new value:
-                                            variablesHash[variabletoassign].variableValue = $rootScope.ruleeffects[executingEvent.event][action.id].data;
-                                        }
-                                    }
-                                });
-                            });
-
-                            //Broadcast rules finished if there was any actual changes to the event.
-                            if(updatedEffectsExits){
-                                $rootScope.$broadcast("ruleeffectsupdated", { event: executingEvent.event });
-                            }
-                        }
-
-                        return true;
-                    });
-                });
-            });
+        executeRules: function(allProgramRules, executingEvent, evs, allDataElements, selectedEntity, selectedEnrollment, flag ) {
+            if(allProgramRules) {
+                var variablesHash = {};
+
+                //Concatenate rules produced by indicator definitions into the other rules:
+                var rules = $filter('filter')(allProgramRules.programRules, {programStageId: null});
+
+                if(executingEvent.programStage){
+                    if(!rules) {
+                        rules = [];
+                    }
+                    rules = rules.concat($filter('filter')(allProgramRules.programRules, {programStageId: executingEvent.programStage}));
+                }
+                if(!rules) {
+                        rules = [];
+                }
+                rules = rules.concat(allProgramRules.programIndicators.rules);
+
+                //Run rules in priority - lowest number first(priority null is last)
+                rules = orderByFilter(rules, 'priority');
+
+                variablesHash = VariableService.getVariables(allProgramRules, executingEvent, evs, allDataElements, selectedEntity, selectedEnrollment);
+
+                if(angular.isObject(rules) && angular.isArray(rules)){
+                    //The program has rules, and we want to run them.
+                    //Prepare repository unless it is already prepared:
+                    if(angular.isUndefined( $rootScope.ruleeffects ) ) {
+                        $rootScope.ruleeffects = {};
+                    }
+
+                    if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event] )){
+                        $rootScope.ruleeffects[executingEvent.event] = {};
+                    }
+
+                    var updatedEffectsExits = false;
+
+                    angular.forEach(rules, function(rule) {
+                        var ruleEffective = false;
+
+                        var expression = rule.condition;
+                        //Go through and populate variables with actual values, but only if there actually is any replacements to be made(one or more "$" is present)
+                        if(expression) {
+                            if(expression.indexOf('{') !== -1) {
+                                expression = replaceVariables(expression, variablesHash);
+                            }
+                            //run expression:
+                            ruleEffective = runExpression(expression, rule.condition, "rule:" + rule.id, flag, variablesHash);
+                        } else {
+                            $log.warn("Rule id:'" + rule.id + "'' and name:'" + rule.name + "' had no condition specified. Please check rule configuration.");
+                        }
+
+                        angular.forEach(rule.programRuleActions, function(action){
+                            //In case the effect-hash is not populated, add entries
+                            if(angular.isUndefined( $rootScope.ruleeffects[executingEvent.event][action.id] )){
+                                $rootScope.ruleeffects[executingEvent.event][action.id] =  {
+                                    id:action.id,
+                                    location:action.location, 
+                                    action:action.programRuleActionType,
+                                    dataElement:action.dataElement,
+                                    content:action.content,
+                                    data:action.data,
+                                    ineffect:undefined
+                                };
+                            }
+
+                            //In case the rule is effective and contains specific data, 
+                            //the effect be refreshed from the variables list.
+                            //If the rule is not effective we can skip this step
+                            if(ruleEffective && action.data)
+                            {
+                                //Preserve old data for comparison:
+                                var oldData = $rootScope.ruleeffects[executingEvent.event][action.id].data;
+                                
+                                //The key data might be containing a dollar sign denoting that the key data is a variable.
+                                //To make a lookup in variables hash, we must make a lookup without the dollar sign in the variable name
+                                //The first strategy is to make a direct lookup. In case the "data" expression is more complex, we have to do more replacement and evaluation.
+
+                                var nameWithoutBrackets = action.data.replace('#{','').replace('}','');
+                                if(angular.isDefined(variablesHash[nameWithoutBrackets]))
+                                {
+                                    //The variable exists, and is replaced with its corresponding value
+                                    $rootScope.ruleeffects[executingEvent.event][action.id].data =
+                                        variablesHash[nameWithoutBrackets].variableValue;
+                                }
+                                else if(action.data.indexOf('{') !== -1)
+                                {
+                                    //Since the value couldnt be looked up directly, and contains a dollar sign, the expression was more complex
+                                    //Now we will have to make a thorough replacement and separate evaluation to find the correct value:
+                                    $rootScope.ruleeffects[executingEvent.event][action.id].data = replaceVariables(action.data, variablesHash);
+                                    //In a scenario where the data contains a complex expression, evaluate the expression to compile(calculate) the result:
+                                    $rootScope.ruleeffects[executingEvent.event][action.id].data = runExpression($rootScope.ruleeffects[executingEvent.event][action.id].data, action.data, "action:" + action.id, flag, variablesHash);
+                                }
+                                
+                                if(oldData !== $rootScope.ruleeffects[executingEvent.event][action.id].data) {
+                                    updatedEffectsExits = true;
+                                }
+                            }
+
+                            //Update the rule effectiveness if it changed in this evaluation;
+                            if($rootScope.ruleeffects[executingEvent.event][action.id].ineffect !== ruleEffective)
+                            {
+                                //There is a change in the rule outcome, we need to update the effect object.
+                                updatedEffectsExits = true;
+                                $rootScope.ruleeffects[executingEvent.event][action.id].ineffect = ruleEffective;
+                            }
+
+                            //In case the rule is of type "assign variable" and the rule is effective,
+                            //the variable data result needs to be applied to the correct variable:
+                            if($rootScope.ruleeffects[executingEvent.event][action.id].action === "ASSIGNVARIABLE" && $rootScope.ruleeffects[executingEvent.event][action.id].ineffect){
+                                //from earlier evaluation, the data portion of the ruleeffect now contains the value of the variable to be assign.
+                                //the content portion of the ruleeffect defines the name for the variable, when dollar is removed:
+                                var variabletoassign = $rootScope.ruleeffects[executingEvent.event][action.id].content.replace("#{","").replace("}","");
+
+                                if(!angular.isDefined(variablesHash[variabletoassign])){
+                                    $log.warn("Variable " + variabletoassign + " was not defined.");
+                                }
+
+                                //Even if the variable is not defined: we assign it:
+                                if(variablesHash[variabletoassign].variableValue !== $rootScope.ruleeffects[executingEvent.event][action.id].data){
+                                    //If the variable was actually updated, we assume that there is an updated ruleeffect somewhere:
+                                    updatedEffectsExits = true;
+                                    //Then we assign the new value:
+                                    variablesHash[variabletoassign].variableValue = $rootScope.ruleeffects[executingEvent.event][action.id].data;
+                                }
+                            }
+                        });
+                    });
+
+                    //Broadcast rules finished if there was any actual changes to the event.
+                    if(updatedEffectsExits){
+                        $rootScope.$broadcast("ruleeffectsupdated", { event: executingEvent.event });
+                    }
+                }
+
+                return true;
+            }
         }
     };
 });
\ No newline at end of file