← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 18630: tracker-capture: period-wise data entry for events

 

------------------------------------------------------------
revno: 18630
committer: Abyot Asalefew Gizaw <abyota@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2015-03-19 11:18:00 +0100
message:
  tracker-capture: period-wise data entry for events
modified:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/event-capture.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/components/dataentry/dataentry.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/new-event.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/report/tei-report-controller.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/controllers.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css
  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/event-capture.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/event-capture.js	2015-03-04 11:34:55 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/scripts/event-capture.js	2015-03-19 10:18:00 +0000
@@ -32,17 +32,6 @@
     objectStores: ['programs', 'programStages', 'geoJsons', 'optionSets', 'events', 'programValidations', 'ouLevels']
 });
 
-(function($) {
-    $.safeEach = function(arr, fn)
-    {
-        if (arr)
-        {
-            $.each(arr, fn);
-        }
-    };
-})(jQuery);
-
-
 /**
  * Page init. The order of events is:
  *

=== 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-03-11 15:02:58 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry-controller.js	2015-03-19 10:18:00 +0000
@@ -43,8 +43,8 @@
                             {color: 'alert-danger', description: 'overdue'},
                             {color: 'alert-default', description: 'skipped'}
                          ];
-    $scope.showEventColors = false;
-    
+    $scope.showEventColors = false;    
+      
     //listen for the selected items
     $scope.$on('dashboardWidgets', function() {        
         $scope.showDataEntryDiv = false;
@@ -152,7 +152,7 @@
     
     $scope.showCreateEvent = function(stage){
         
-        var dummyEvent = EventUtils.createDummyEvent($scope.eventsByStage[stage.id], stage, $scope.selectedOrgUnit, $scope.selectedEnrollment);
+        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',
@@ -164,11 +164,8 @@
                 dummyEvent: function(){
                     return dummyEvent;
                 },
-                programId: function () {
-                    return $scope.selectedProgram.id;
-                },
-                trackedEntityInstanceId: function(){
-                    return $scope.selectedEntity.trackedEntityInstance;
+                eventPeriods: function(){
+                    return $scope.eventPeriods;
                 }
             }
         });
@@ -187,9 +184,10 @@
                 
                 if(dummyEvent.coordinate){
                     newEvent.coordinate = {};
-                }                
+                }
                 
                 $scope.eventsByStage[newEvent.programStage].push(newEvent);
+                sortEventsByStage();
                 $scope.showDataEntry(newEvent, false);
             }            
         }, function () {
@@ -415,6 +413,7 @@
     };
     
     $scope.saveDueDate = function(){
+        
         $scope.dueDateSaved = false;
 
         if($scope.currentEvent.dueDate === ''){
@@ -440,10 +439,23 @@
              trackedEntityInstance: $scope.currentEvent.trackedEntityInstance
             };
         
+        if($scope.currentStage.periodType){
+            e.eventDate = e.dueDate;
+        }
+        
+        if($scope.currentEvent.coordinate){
+            e.coordinate = $scope.currentEvent.coordinate;
+        }
+            
         DHIS2EventFactory.update(e).then(function(data){            
             $scope.invalidDate = false;
             $scope.dueDateSaved = true;
-            $scope.currentEvent.sortingDate = $scope.currentEvent.dueDate;
+            
+            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.schedulingEnabled = !$scope.schedulingEnabled;
             sortEventsByStage();
@@ -682,22 +694,29 @@
     };
     
     var sortEventsByStage = function(){
+        
         $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) {
                     return DateUtils.getDate(event.sortingDate);
-                });                
-                var periods = PeriodService.getPeriods(sortedEvents, stage);
+                }, true);
+                
+                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.showDataEntryForEvent = function(period){
+    $scope.showDataEntryForEvent = function(period){        
         var event = null;
         for(var i=0; i<$scope.eventsByStage[period.stage].length; i++){
             if($scope.eventsByStage[period.stage][i].event === period.event){
@@ -744,16 +763,21 @@
             DialogService,
             stagesById,
             dummyEvent,
-            programId,
-            trackedEntityInstanceId){
+            eventPeriods){
     $scope.stagesById = stagesById;
     $scope.programStageId = dummyEvent.programStage;
-    $scope.programId = programId;
-    $scope.orgUnitId = dummyEvent.orgUnit;    
-    $scope.trackedEntityInstanceId = trackedEntityInstanceId;
+    $scope.eventPeriods = eventPeriods;
+    $scope.selectedStage =  $scope.stagesById[dummyEvent.programStage];
     
     $scope.dhis2Event = {eventDate: '', dueDate: dummyEvent.dueDate, reportDateDescription: dummyEvent.reportDateDescription, name: dummyEvent.name, invalid: true};
     
+    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;
     
@@ -785,14 +809,20 @@
             return false;
         }
         
+        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: $scope.trackedEntityInstanceId,
-                program: $scope.programId,
-                programStage: $scope.programStageId,
-                orgUnit: $scope.orgUnitId,                        
+                trackedEntityInstance: dummyEvent.trackedEntityInstance,
+                program: dummyEvent.program,
+                programStage: dummyEvent.programStage,
+                enrollment: dummyEvent.enrollment,
+                orgUnit: dummyEvent.orgUnit,                        
                 dueDate: dueDate,
                 eventDate: eventDate,
                 notes: [],
@@ -800,7 +830,6 @@
                 status: 'ACTIVE'
             };            
         newEvents.events.push(newEvent);
-        
         DHIS2EventFactory.create(newEvents).then(function(data){
             if (data.importSummaries[0].status === 'ERROR') {
                 var dialogOptions = {

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry.html	2015-03-11 11:21:40 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/dataentry.html	2015-03-19 10:18:00 +0000
@@ -72,7 +72,8 @@
 
         <form name="outerForm" novalidate>
             <div ng-if="currentEvent">
-                <!-- event dates begin -->
+                
+                <!-- event dates/scheduling begin -->
                 <div class="row">
                     <div class="col-md-6">
                         {{currentEvent.reportDateDescription}}
@@ -105,7 +106,7 @@
                         <span ng-if="invalidDate" class="error">{{'date_required'| translate}}</span>
                     </div>                        
                 </div>
-                <!-- event dates end -->
+                <!-- event dates/scheduling end -->
 
                 <!-- coordinates begin -->
                 <div class="row" ng-if="currentStage.captureCoordinates && currentEvent.eventDate">
@@ -140,11 +141,14 @@
                 </div>
                 <!-- coordinates begin -->
 
+                <!-- data entry form begins -->
                 <div ng-if="currentEvent.eventDate">                            
                     <div class="clear vertical-spacing" ng-if="displayCustomForm" ng-include="'../dhis-web-commons/customform/custom-form.html'"></div>  
                     <div class="clear vertical-spacing" ng-if="!displayCustomForm" ng-include="'components/dataentry/default-form.html'"></div>
                 </div>            
-
+                <!-- data entry form ends -->
+                
+                <!-- data entry/event buttons begins -->
                 <div class="form-group">
                     <div class='row'><hr></div>
                     <a href ng-click="completeIncompleteEvent()" 
@@ -167,6 +171,8 @@
                        ng-disabled="currentEvent.enrollmentStatus === 'COMPLETED' || currentEvent.editingNotAllowed"
                        class="btn btn-danger">{{'delete'| translate}}</a>
                 </div>
+                <!-- data entry/event buttons ends -->
+                
             </div>
         </form>
 

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/new-event.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/new-event.html	2015-02-20 12:08:37 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/dataentry/new-event.html	2015-03-19 10:18:00 +0000
@@ -3,31 +3,47 @@
 </div>
 <div class="modal-body page">
     <form name="eventCreationForm" class="form-horizontal" role="form" novalidate>
-        <div class="form-group">
-            <label class="col-sm-2 control-label">{{'due_date'| translate}}</label>
-            <div class="col-sm-10">
-                <input type="text" 
-                       class="form-control" 
-                       name="dueDate"
-                       placeholder="{{dhis2CalendarFormat.keyDateFormat}}" 
-                       ng-rquired="true" 
-                       d2-date
-                       ng-model="dhis2Event.dueDate">
-                <span ng-if="dueDateInvalid" class="error">{{'required'| translate}}</span>
+        <div ng-if="!selectedStage.periodType">
+            <div class="form-group">
+                <label class="col-sm-2 control-label">{{'due_date'| translate}}</label>
+                <div class="col-sm-10">
+                    <input type="text" 
+                           class="form-control" 
+                           name="dueDate"
+                           placeholder="{{dhis2CalendarFormat.keyDateFormat}}" 
+                           ng-rquired="true" 
+                           d2-date
+                           ng-model="dhis2Event.dueDate">
+                    <span ng-if="dueDateInvalid" class="error">{{'required'| translate}}</span>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="col-sm-2 control-label">{{dhis2Event.reportDateDescription}}</label>
+                <div class="col-sm-10">
+                    <input type="text" 
+                           class="form-control" 
+                           name="eventDate"
+                           placeholder="{{dhis2CalendarFormat.keyDateFormat}}" 
+                           ng-rquired="false" 
+                           d2-date
+                           ng-model="dhis2Event.eventDate">
+                    <span ng-if="eventDateInvalid" class="error">{{'required'| translate}}</span>
+                </div>
             </div>
         </div>
-        <div class="form-group">
-            <label class="col-sm-2 control-label">{{dhis2Event.reportDateDescription}}</label>
-            <div class="col-sm-10">
-                <input type="text" 
-                       class="form-control" 
-                       name="eventDate"
-                       placeholder="{{dhis2CalendarFormat.keyDateFormat}}" 
-                       ng-rquired="false" 
-                       d2-date
-                       ng-model="dhis2Event.eventDate">
-                <span ng-if="eventDateInvalid" class="error">{{'required'| translate}}</span>
-            </div>
+        <div ng-if="selectedStage.periodType">
+            <div class="form-group">
+                <label class="control-label">
+                    {{'period'| translate}}
+                </label>                
+                <select class="form-control-program" 
+                        ng-model="dhis2Event.selectedPeriod"
+                        ng-options="period.name for period in dhis2Event.periods">
+                </select>                
+                <button ng-disabled="true" type="button" class="btn btn-default small-horizonal-spacing trim" ng-click="fetchPeriod('PREV')" title="{{'prev_period'| translate}}"><i class="fa fa-backward"></i></button>        
+                <button ng-disabled="true" type="button" class="btn btn-default small-horizonal-spacing trim" ng-click="fetchPeriod('NEXT')" title="{{'nxt_period'| translate}}"><i class="fa fa-forward"></i></button>        
+        
+            </div>            
         </div>
     </form>  
 </div>

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/report/tei-report-controller.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/report/tei-report-controller.js	2015-03-16 14:15:34 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/components/report/tei-report-controller.js	2015-03-19 10:18:00 +0000
@@ -120,7 +120,7 @@
                         providedElsewhereExists = true;
                         $scope.allowProvidedElsewhereExists[st.id] = true;
                     }                
-                }            
+                }
             });
         });
         

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties	2015-03-19 03:43:44 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/i18n/i18n_app.properties	2015-03-19 10:18:00 +0000
@@ -291,6 +291,7 @@
 completed_enrollment=Only those with completed enrollment
 filter_events=Filter events
 list_events=List all events
+period=Period
 jan=January
 feb=February
 mar=March
@@ -302,4 +303,5 @@
 sep=September
 oct=October
 nov=November
-dec=December
\ No newline at end of file
+dec=December
+week=Week
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html	2015-03-05 15:48:50 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html	2015-03-19 10:18:00 +0000
@@ -20,6 +20,7 @@
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/ui/jquery-ui.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.plugin.min.js"></script>
         
+        
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/calendars/jquery.calendars.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/calendars/jquery.calendars.picker.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/calendars/jquery.calendars.plus.min.js"></script>
@@ -61,8 +62,7 @@
         <script type="text/javascript" src="../dhis-web-commons/javascripts/commons.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/commons.ajax.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/lists.js"></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/periodType.js"></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/date.js"></script>
+        <script type="text/javascript" src="../dhis-web-commons/javascripts/periodTypeNoDep.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/json2.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/validationRules.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.array.js"></script>

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/controllers.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/controllers.js	2015-03-12 10:55:00 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/controllers.js	2015-03-19 10:18:00 +0000
@@ -263,7 +263,7 @@
             $scope.doSearch = true;
             
             if(!$scope.sortColumn.id){                                      
-                $scope.sortGrid({id: 'created', name: $translate('registration_date'), valueType: 'date', displayInListNoProgram: false, showFilter: false, show: true});
+                $scope.sortGrid({id: 'created', name: $translate('registration_date'), valueType: 'date', displayInListNoProgram: false, showFilter: false, show: false});
             }
         });
     };

=== 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-03-16 14:14:10 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/services.js	2015-03-19 10:18:00 +0000
@@ -1,4 +1,4 @@
-/* global angular */
+/* global angular, moment, dhis2 */
 
 'use strict';
 
@@ -51,52 +51,11 @@
 })
 
 /* current selections */
-.service('PeriodService', function($translate, CalendarService){
+.service('PeriodService', function($translate, $filter, DateUtils, CalendarService){
     
     var calendarSetting = CalendarService.getSetting();    
-    var months = [
-                    $translate('jan'), 
-                    $translate('feb'),
-                    $translate('mar'),
-                    $translate('apr'),
-                    $translate('may'),
-                    $translate('jun'),
-                    $translate('jul'),
-                    $translate('aug'),                    
-                    $translate('sep'),
-                    $translate('oct'),
-                    $translate('nov'),
-                    $translate('dec')
-                  ];    
-   
-    this.getMonths = function(){
-        return months;
-    };
-    
-    this.getPeriods = function(events, stage){
-        var periods = [];
-        if(stage){            
-            angular.forEach(events, function(event){
-                periods.push({event: event.event, name: event.sortingDate, stage: stage.id});
-            });
-            /*if(stage.standardInterval === 30){
-                angular.forEach(events, function(event){
-                    var obj = {year: moment(event.sortingDate, calendarSetting.momentFormat).year(), month: moment(event.sortingDate, calendarSetting.momentFormat).month(), week: moment(event.sortingDate, calendarSetting.momentFormat).week(), day: moment(event.sortingDate, calendarSetting.momentFormat).day()};                
-                    periods.push({event: event.event, name: months[obj.month] + ' ' + obj.year, stage: stage.id});
-                });
-            }
-            else{
-                angular.forEach(events, function(event){
-                    periods.push({event: event.event, name: event.sortingDate, stage: stage.id});
-                });
-            }*/            
-        }
-        
-        return periods;
-    };
-    
-    
-    this.splitDate = function(dateValue){
+    
+    var splitDate = function(dateValue){
         if(!dateValue){
             return;
         }
@@ -104,6 +63,53 @@
 
         return {year: moment(dateValue, calendarSetting.momentFormat).year(), month: moment(dateValue, calendarSetting.momentFormat).month(), week: moment(dateValue, calendarSetting.momentFormat).week(), day: moment(dateValue, calendarSetting.momentFormat).day()};
     };
+    
+    this.getPeriods = function(events, stage, enrollment){
+        
+        if(!stage){
+            return;
+        }
+        
+        var referenceDate = enrollment.dateOfIncident ? enrollment.dateOfIncident : enrollment.dateOfEnrollment;
+        var offset = stage.minDaysFromStart;
+        
+        if(stage.generatedByEnrollmentDate){
+            referenceDate = enrollment.dateOfEnrollment;
+        }        
+               
+        var occupiedPeriods = [];
+        var availablePeriods = [];
+        if(!stage.periodType){
+            angular.forEach(events, function(event){
+                occupiedPeriods.push({event: event.event, name: event.sortingDate, stage: stage.id});
+            });            
+            
+        }
+        else{
+
+            var startDate = DateUtils.format( moment(referenceDate, calendarSetting.momentFormat).add(offset, 'days') );
+            var periodOffet = splitDate(DateUtils.getToday()).year - splitDate(startDate).year;
+
+            //generate availablePeriods
+            var pt = new PeriodType();
+            var d2Periods = pt.get(stage.periodType).generatePeriods({offset: periodOffet, filterFuturePeriods: false, reversePeriods: false});
+            angular.forEach(d2Periods, function(p){
+                p.endDate = DateUtils.formatFromApiToUser(p.endDate);
+                p.startDate = DateUtils.formatFromApiToUser(p.startDate);
+                availablePeriods[p.endDate] = p;
+            });                
+
+            //get occupied periods
+            angular.forEach(events, function(event){
+                var p = availablePeriods[event.sortingDate];
+                if(p){
+                    occupiedPeriods.push({event: event.event, name: p.name, stage: stage.id, eventDate: event.sortingDate});
+                    delete availablePeriods[event.sortingDate];
+                }                    
+            });
+        }
+        return {occupiedPeriods: occupiedPeriods, availablePeriods: availablePeriods};
+    };
 })
 
 /* Factory to fetch optionSets */
@@ -1214,8 +1220,7 @@
                 column.show = false;                
                 if( (column.id === 'orgUnitName' && ouMode !== 'SELECTED') ||
                     column.displayInListNoProgram || 
-                    column.displayInList || 
-                    column.id === 'created'){
+                    column.displayInList){
                     column.show = true;    
                 }                
                 column.showFilter = false;                
@@ -1251,12 +1256,14 @@
     };
 })
 
-.service('EventUtils', function(DateUtils, CalendarService, OptionSetService, $filter, orderByFilter){
+.service('EventUtils', function(DateUtils, PeriodService, CalendarService, OptionSetService, $filter, orderByFilter){
     
-    var getEventDueDate = function(eventsByStage, programStage, enrollment){            
+    var getEventDueDate = function(eventsByStage, programStage, enrollment){       
+        
         var referenceDate = enrollment.dateOfIncident ? enrollment.dateOfIncident : enrollment.dateOfEnrollment,
             offset = programStage.minDaysFromStart,
-            calendarSetting = CalendarService.getSetting();
+            calendarSetting = CalendarService.getSetting(),
+            dueDate;
 
         if(programStage.generatedByEnrollmentDate){
             referenceDate = enrollment.dateOfEnrollment;
@@ -1271,30 +1278,71 @@
             });
 
             if(evs.length > 0){
-                evs = orderByFilter(evs, '-eventDate');
-                referenceDate = evs[0].eventDate;                
-                offset = programStage.standardInterval ? programStage.standardInterval : 0;
-            }
+                evs = orderByFilter(evs, '-eventDate');                
+                if(programStage.periodType){
+                    
+                }
+                else{
+                    referenceDate = evs[0].eventDate;
+                    offset = programStage.standardInterval;
+                }
+            }                
         }
-
-        var dueDate = moment(referenceDate, calendarSetting.momentFormat).add('d', offset)._d;
-        dueDate = $filter('date')(dueDate, calendarSetting.keyDateFormat); 
+        dueDate = moment(referenceDate, calendarSetting.momentFormat).add('d', offset)._d;
+        dueDate = $filter('date')(dueDate, calendarSetting.keyDateFormat);        
         return dueDate;
     };
     
+    var getEventDuePeriod = function(eventsByStage, programStage, enrollment){ 
+        
+        var evs = [];                
+        angular.forEach(eventsByStage, function(ev){
+            if(ev.eventDate){
+                evs.push(ev);
+            }
+        });
+
+        if(evs.length > 0){
+            evs = orderByFilter(evs, '-eventDate');
+        }
+        
+        var availabelPeriods = PeriodService.getPeriods(evs,programStage, enrollment).availablePeriods;
+        var periods = [];
+        for(var k in availabelPeriods){
+            if(availabelPeriods.hasOwnProperty(k)){
+                periods.push( availabelPeriods[k] );
+            }
+        }        
+        return periods;
+    };
+    
     return {
-        createDummyEvent: function(eventsPerStage, programStage, orgUnit, enrollment){
-            var today = DateUtils.getToday();    
-            var dueDate = getEventDueDate(eventsPerStage, programStage, enrollment);
-            var dummyEvent = {programStage: programStage.id, 
+        createDummyEvent: function(eventsPerStage, tei, program, programStage, orgUnit, enrollment){
+            var today = DateUtils.getToday();
+            var dummyEvent = {trackedEntityInstance: tei.trackedEntityInstance, 
+                              programStage: programStage.id, 
+                              program: program.id,
                               orgUnit: orgUnit.id,
                               orgUnitName: orgUnit.name,
-                              dueDate: dueDate,
-                              sortingDate: dueDate,
                               name: programStage.name,
                               reportDateDescription: programStage.reportDateDescription,
                               enrollmentStatus: 'ACTIVE',
+                              enrollment: enrollment.enrollment,
                               status: 'SCHEDULED'};
+                          
+            if(programStage.periodType){                
+                var periods = getEventDuePeriod(eventsPerStage, programStage, enrollment);
+                dummyEvent.dueDate = periods[0].endDate;
+                dummyEvent.periodName = periods[0].name;
+                dummyEvent.eventDate = dummyEvent.dueDate;
+                dummyEvent.periods = periods;
+            }
+            else{
+                dummyEvent.dueDate = getEventDueDate(eventsPerStage, programStage, enrollment);
+            }
+            
+            dummyEvent.sortingDate = dummyEvent.dueDate;
+            
             
             if(programStage.captureCoordinates){
                 dummyEvent.coordinate = {};
@@ -1342,10 +1390,20 @@
                                 program: program.id,
                                 programStage: stage.id,
                                 orgUnit: orgUnit.id,
-                                dueDate: DateUtils.formatFromUserToApi(getEventDueDate(null,stage, enrollment)),
+                                enrollment: enrollment.enrollment,                                
                                 status: 'SCHEDULE'
                             };
-                            
+                        if(stage.periodType){
+                            var periods = getEventDuePeriod(null, stage, enrollment);
+                            newEvent.dueDate = DateUtils.formatFromUserToApi(periods[0].dueDate);;
+                            newEvent.eventDate = newEvent.dueDate;
+                            //newEvent.periodName = periods[0].name;                            
+                            //newEvent.periods = periods;
+                        }
+                        else{
+                            newEvent.dueDate = DateUtils.formatFromUserToApi(getEventDueDate(null,stage, enrollment));
+                        }
+                        
                         if(stage.openAfterEnrollment){
                             if(stage.reportDateToUse === 'dateOfIncident'){
                                 newEvent.eventDate = DateUtils.formatFromUserToApi(enrollment.dateOfIncident);

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js	2015-03-16 14:14:10 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/tracker-capture.js	2015-03-19 10:18:00 +0000
@@ -29,16 +29,6 @@
     objectStores: ['programs', 'programStages', 'trackedEntities', 'trackedEntityForms', 'attributes', 'relationshipTypes', 'optionSets', 'programValidations', 'ouLevels']      
 });
 
-(function($) {
-    $.safeEach = function(arr, fn)
-    {
-        if (arr)
-        {
-            $.each(arr, fn);
-        }
-    };
-})(jQuery);
-
 /**
  * Page init. The order of events is:
  *

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css	2015-03-11 15:02:58 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/styles/style.css	2015-03-19 10:18:00 +0000
@@ -90,7 +90,6 @@
 .vertical-center {
     line-height: inherit;
     vertical-align: middle;
-
 }
 
 .empty-event-container {
@@ -1011,4 +1010,9 @@
 
 .mouse-pointer {    
     cursor: pointer;
+}
+
+ .form-horizontal .control-label {
+    display: table-cell;
+    vertical-align: middle;
 }
\ No newline at end of file

=== 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-02-12 14:25:04 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js	2015-03-19 10:18:00 +0000
@@ -237,93 +237,98 @@
                             if(attributes.hasOwnProperty('name')){
                                 attributes['name'] = fieldId;
                             }
+                            
+                            var prStDe = programStageDataElements[fieldId];
+                            
+                            if( prStDe && prStDe.dataElement && prStDe.dataElement.type ){                            
 
-                            //check data element type and generate corresponding angular input field
-                            if(programStageDataElements[fieldId].dataElement.type === "int"){
-                                newInputField = '<input type="text" ' +
-                                                this.getAttributesAsString(attributes) +
-                                                ' d2-validation ' +
-                                                ' d2-number-validation ' +
-                                                ' number-type="' + programStageDataElements[fieldId].dataElement.numberType + '" ' +
-                                                ' ng-model="currentEvent.' + fieldId + '"' +
-                                                ' input-field-id="' + fieldId + '"' +
-                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
-                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
-                                                ' ng-blur="saveDatavalue(prStDes.'+ fieldId + ')"' + 
-                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}">';
-                            }
-                            if(programStageDataElements[fieldId].dataElement.type === "string"){
-                                if(programStageDataElements[fieldId].dataElement.optionSet){
-                                    var optionSetId = programStageDataElements[fieldId].dataElement.optionSet.id;
-                                    newInputField = '<input type="text" ' +
-                                                this.getAttributesAsString(attributes) +
-                                                ' d2-validation ' +
-                                                ' ng-model="currentEvent.' + fieldId + '" ' +
-                                                ' input-field-id="' + fieldId + '"' +
-                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent[uid]==\'uid\' || currentEvent.editingNotAllowed"' +
-                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}"' +
-                                                ' typeahead="option.name as option.name for option in optionSets.'+optionSetId+'.options | filter:$viewValue | limitTo:20"' +
-                                                ' typeahead-editable="false" ' +
-                                                ' d2-typeahead-validation ' +
-                                                ' class="typeahead" ' +
-                                                ' placeholder="&#xf0d7;&nbsp;&nbsp;" ' +
-                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +                                            
-                                                ' ng-blur="saveDatavalue(prStDes.'+ fieldId + ')"' +
-                                                ' typeahead-open-on-focus ng-required="prStDes.'+fieldId+'.compulsory"> ';
-                                }
-                                else{
-                                    newInputField = '<input type="text" ' +
-                                                this.getAttributesAsString(attributes) +
-                                                ' d2-validation ' +
-                                                ' ng-model="currentEvent.' + fieldId + '" ' +
-                                                ' input-field-id="' + fieldId + '"' +
-                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent[uid]==\'uid\' || currentEvent.editingNotAllowed"' +
-                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
-                                                ' ng-blur="saveDatavalue(prStDes.'+ fieldId + ')"' +
-                                                ' ng-required="prStDes.' + fieldId + '.compulsory"> ';                                     
-                                }
-                            }
-                            if(programStageDataElements[fieldId].dataElement.type === "bool"){
-                                newInputField = '<select ' +
-                                                this.getAttributesAsString(attributes) +
-                                                ' d2-validation ' +
-                                                ' ng-model="currentEvent.' + fieldId + '" ' +
-                                                ' input-field-id="' + fieldId + '"' +
-                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
-                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
-                                                ' ng-change="saveDatavalue(prStDes.'+ fieldId + ')"' + 
-                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}">' + 
-                                                '<option value="">{{\'please_select\'| translate}}</option>' +
-                                                '<option value="false">{{\'no\'| translate}}</option>' + 
-                                                '<option value="true">{{\'yes\'| translate}}</option>' +
-                                                '</select> ';                                     
-                            }
-                            if(programStageDataElements[fieldId].dataElement.type === "date"){
-                                var maxDate = programStageDataElements[fieldId].allowFutureDate ? '' : 0;
-                                newInputField = '<input type="text" ' +
-                                                this.getAttributesAsString(attributes) +
-                                                ' d2-validation ' +
-                                                ' ng-model="currentEvent.' + fieldId + '"' +
-                                                ' input-field-id="' + fieldId + '"' +                                                
-                                                ' placeholder="{{dhis2CalendarFormat.keyDateFormat}}" ' +
-                                                ' d2-date ' +
-                                                ' max-date="' + maxDate + '"' +
-                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
-                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
-                                                ' blur-or-change="saveDatavalue(prStDes.'+ fieldId + ')"' + 
-                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}"> '; 
-                            }
-                            if(programStageDataElements[fieldId].dataElement.type === "trueOnly"){
-                                newInputField = '<input type="checkbox" ' +
-                                                this.getAttributesAsString(attributes) +
-                                                ' d2-validation ' +
-                                                ' ng-model="currentEvent.' + fieldId + '"' +
-                                                ' input-field-id="' + fieldId + '"' +
-                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
-                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
-                                                ' ng-change="saveDatavalue(prStDes.'+ fieldId + ')"' +
-                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}"> ';
-                            }                            
+	                            //check data element type and generate corresponding angular input field
+	                            if(prStDe.dataElement.type === "int"){
+	                                newInputField = '<input type="text" ' +
+	                                                this.getAttributesAsString(attributes) +
+	                                                ' d2-validation ' +
+	                                                ' d2-number-validation ' +
+	                                                ' number-type="' + prStDe.dataElement.numberType + '" ' +
+	                                                ' ng-model="currentEvent.' + fieldId + '"' +
+	                                                ' input-field-id="' + fieldId + '"' +
+	                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
+	                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
+	                                                ' ng-blur="saveDatavalue(prStDes.'+ fieldId + ')"' + 
+	                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}">';
+	                            }
+	                            if(prStDe.dataElement.type === "string"){
+	                                if(prStDe.dataElement.optionSet){
+	                                    var optionSetId = prStDe.dataElement.optionSet.id;
+	                                    newInputField = '<input type="text" ' +
+	                                                this.getAttributesAsString(attributes) +
+	                                                ' d2-validation ' +
+	                                                ' ng-model="currentEvent.' + fieldId + '" ' +
+	                                                ' input-field-id="' + fieldId + '"' +
+	                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent[uid]==\'uid\' || currentEvent.editingNotAllowed"' +
+	                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}"' +
+	                                                ' typeahead="option.name as option.name for option in optionSets.'+optionSetId+'.options | filter:$viewValue | limitTo:20"' +
+	                                                ' typeahead-editable="false" ' +
+	                                                ' d2-typeahead-validation ' +
+	                                                ' class="typeahead" ' +
+	                                                ' placeholder="&#xf0d7;&nbsp;&nbsp;" ' +
+	                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +                                            
+	                                                ' ng-blur="saveDatavalue(prStDes.'+ fieldId + ')"' +
+	                                                ' typeahead-open-on-focus ng-required="prStDes.'+fieldId+'.compulsory"> ';
+	                                }
+	                                else{
+	                                    newInputField = '<input type="text" ' +
+	                                                this.getAttributesAsString(attributes) +
+	                                                ' d2-validation ' +
+	                                                ' ng-model="currentEvent.' + fieldId + '" ' +
+	                                                ' input-field-id="' + fieldId + '"' +
+	                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent[uid]==\'uid\' || currentEvent.editingNotAllowed"' +
+	                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
+	                                                ' ng-blur="saveDatavalue(prStDes.'+ fieldId + ')"' +
+	                                                ' ng-required="prStDes.' + fieldId + '.compulsory"> ';                                     
+	                                }
+	                            }
+	                            if(prStDe.dataElement.type === "bool"){
+	                                newInputField = '<select ' +
+	                                                this.getAttributesAsString(attributes) +
+	                                                ' d2-validation ' +
+	                                                ' ng-model="currentEvent.' + fieldId + '" ' +
+	                                                ' input-field-id="' + fieldId + '"' +
+	                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
+	                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
+	                                                ' ng-change="saveDatavalue(prStDes.'+ fieldId + ')"' + 
+	                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}">' + 
+	                                                '<option value="">{{\'please_select\'| translate}}</option>' +
+	                                                '<option value="false">{{\'no\'| translate}}</option>' + 
+	                                                '<option value="true">{{\'yes\'| translate}}</option>' +
+	                                                '</select> ';                                     
+	                            }
+	                            if(prStDe.dataElement.type === "date"){
+	                                var maxDate = prStDe.allowFutureDate ? '' : 0;
+	                                newInputField = '<input type="text" ' +
+	                                                this.getAttributesAsString(attributes) +
+	                                                ' d2-validation ' +
+	                                                ' ng-model="currentEvent.' + fieldId + '"' +
+	                                                ' input-field-id="' + fieldId + '"' +                                                
+	                                                ' placeholder="{{dhis2CalendarFormat.keyDateFormat}}" ' +
+	                                                ' d2-date ' +
+	                                                ' max-date="' + maxDate + '"' +
+	                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
+	                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
+	                                                ' blur-or-change="saveDatavalue(prStDes.'+ fieldId + ')"' + 
+	                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}"> '; 
+	                            }
+	                            if(prStDe.dataElement.type.type === "trueOnly"){
+	                                newInputField = '<input type="checkbox" ' +
+	                                                this.getAttributesAsString(attributes) +
+	                                                ' d2-validation ' +
+	                                                ' ng-model="currentEvent.' + fieldId + '"' +
+	                                                ' input-field-id="' + fieldId + '"' +
+	                                                ' ng-class="getInputNotifcationClass(prStDes.' + fieldId + '.dataElement.id,true)"' +
+	                                                ' ng-disabled="selectedEnrollment.status===\'CANCELLED\' || selectedEnrollment.status===\'COMPLETED\' || currentEvent.editingNotAllowed"' +
+	                                                ' ng-change="saveDatavalue(prStDes.'+ fieldId + ')"' +
+	                                                ' ng-required="{{prStDes.' + fieldId + '.compulsory}}"> ';
+	                            }
+                           	}                            
                         }
 						
                         newInputField = newInputField + ' <span ng-show="(outerForm.'+ fieldId +'.$dirty && outerForm.'+ fieldId +'.$invalid) || (outerForm.submitted && outerForm.'+ fieldId +'.$invalid) || (currentEvent.' + fieldId + ' && outerForm.' + fieldId + '.$invalid)" class="required">{{getErrorMessage(' + errorMessageId + ')}}</span> ';
@@ -371,105 +376,111 @@
                         var fieldName = attId;                        
                         var attMaxDate = trackedEntityFormAttributes[attId].allowFutureDate ? '' : 0;
                         
-                        //check attribute type and generate corresponding angular input field
-                        if(trackedEntityFormAttributes[attId].valueType === "number"){
-                            newInputField = '<input type="text" ' +
-                                            ' name="' + fieldName + '"' +                          
-                                            ' element-id="' + i + '"' +
-                                            this.getAttributesAsString(attributes) +
-                                            ' d2-validation ' +
-                                            ' d2-number-validation ' +
-                                            ' d2-focus-next-on-enter' + 
-                                            ' ng-model="selectedTei.' + attId + '" ' +
-                                            ' ng-disabled="editingDisabled"' +
-                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
-                                            ' ng-required=" ' + trackedEntityFormAttributes[attId].mandatory + '"> ';
-                        }                                               
-                        else if(trackedEntityFormAttributes[attId].valueType === "optionSet"){
-                            var optionSetId = trackedEntityFormAttributes[attId].optionSet.id;                            
-                            newInputField = '<input type="text" ' +
-                                            ' name="' + fieldName + '"' +
-                                            ' element-id="' + i + '"' +
-                                            this.getAttributesAsString(attributes) +
-                                            ' d2-focus-next-on-enter' + 
-                                            ' ng-model="selectedTei.' + attId + '" ' +
-                                            ' ng-disabled="editingDisabled"' +
-                                            ' d2-validation ' +
-                                            ' d2-typeahead-validation ' +
-                                            ' class="typeahead" ' +
-                                            ' placeholder="&#xf0d7;&nbsp;&nbsp;" ' +
-                                            ' typeahead-editable="false" ' + 
-                                            ' typeahead="option.name as option.name for option in optionSets.' + optionSetId + '.options | filter:$viewValue | limitTo:50"' +
-                                            ' typeahead-open-on-focus ' +
-                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
-                                            ' ng-required=" ' + trackedEntityFormAttributes[attId].mandatory + '"> ';                            
-                        }
-                        else if(trackedEntityFormAttributes[attId].valueType === "bool"){
-                            newInputField = '<select ' +
-                                            ' name="' + fieldName + '"' +
-                                            ' element-id="' + i + '"' +
-                                            this.getAttributesAsString(attributes) +
-                                            ' d2-focus-next-on-enter' + 
-                                            ' ng-model="selectedTei.' + attId + '" ' +
-                                            ' ng-disabled="editingDisabled"' +
-                                            ' ng-change="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
-                                            ' ng-required=" ' + trackedEntityFormAttributes[attId].mandatory + '"> ' +
-                                            ' <option value="">{{\'please_select\'| translate}}</option>' +
-                                            ' <option value="false">{{\'no\'| translate}}</option>' + 
-                                            ' <option value="true">{{\'yes\'| translate}}</option>' +
-                                            '</select> ';
-                        }
-                        else if(trackedEntityFormAttributes[attId].valueType === "date"){
-                            newInputField = '<input type="text" ' +
-                                            ' name="' + fieldName + '"' +
-                                            ' element-id="' + i + '"' +
-                                            this.getAttributesAsString(attributes) +
-                                            ' d2-focus-next-on-enter' + 
-                                            ' placeholder="{{dhis2CalendarFormat.keyDateFormat}}" ' +
-                                            ' ng-model="selectedTei.' + attId + '" ' +
-                                            ' ng-disabled="editingDisabled"' +
-                                            ' max-date="' + attMaxDate + '"' + '\'' +
-                                            ' d2-date' +
-                                            ' d2-validation ' +
-                                            ' blur-or-change="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
-                                            ' ng-required=" ' + trackedEntityFormAttributes[attId].mandatory + '"> ';
-                        }
-                        else if(trackedEntityFormAttributes[attId].valueType === "trueOnly"){
-                            newInputField = '<input type="checkbox" ' +  
-                                            ' name="' + fieldName + '"' +
-                                            ' element-id="' + i + '"' +
-                                            this.getAttributesAsString(attributes) + 
-                                            ' d2-validation ' +
-                                            ' d2-focus-next-on-enter' + 
-                                            ' ng-model="selectedTei.' + attId + '" ' +
-                                            ' ng-disabled="editingDisabled"' +
-                                            ' ng-change="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
-                                            ' ng-required=" ' + trackedEntityFormAttributes[attId].mandatory + '"> ';
-                        }
-                        else if(trackedEntityFormAttributes[attId].valueType === "email"){
-                            newInputField = '<input type="email" ' +    
-                                            ' name="' + fieldName + '"' +                                              
-                                            ' element-id="' + i + '"' +
-                                            this.getAttributesAsString(attributes) +
-                                            ' d2-validation ' +
-                                            ' d2-focus-next-on-enter' + 
-                                            ' ng-model="selectedTei.' + attId + '" ' +
-                                            ' ng-disabled="editingDisabled"' +
-                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
-                                            ' ng-required=" ' + trackedEntityFormAttributes[attId].mandatory + '"> ';
-                        }
-                        else {
-                            newInputField = '<input type="text" ' +
-                                            ' name="' + fieldName + '"' +
-                                            ' element-id="' + i + '"' +                                             
-                                            this.getAttributesAsString(attributes) +
-                                            ' d2-validation ' +
-                                            ' d2-focus-next-on-enter' + 
-                                            ' ng-model="selectedTei.' + attId + '" ' +
-                                            ' ng-disabled="editingDisabled"' +
-                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
-                                            ' ng-required=" ' + trackedEntityFormAttributes[attId].mandatory + '"> ';
-                        } 
+                        var att = trackedEntityFormAttributes[attId];
+                        
+                        if( att ){                       
+                        
+	                        //check attribute type and generate corresponding angular input field
+	                        if(att.valueType === "number"){
+	                            newInputField = '<input type="text" ' +
+	                                            ' name="' + fieldName + '"' +                          
+	                                            ' element-id="' + i + '"' +
+	                                            this.getAttributesAsString(attributes) +
+	                                            ' d2-validation ' +
+	                                            ' d2-number-validation ' +
+	                                            ' d2-focus-next-on-enter' + 
+	                                            ' ng-model="selectedTei.' + attId + '" ' +
+	                                            ' ng-disabled="editingDisabled"' +
+	                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
+	                                            ' ng-required=" ' + att.mandatory + '"> ';
+	                        }                                               
+	                        else if(att.valueType === "optionSet"){
+	                            var optionSetId = att.optionSet.id;                            
+	                            newInputField = '<input type="text" ' +
+	                                            ' name="' + fieldName + '"' +
+	                                            ' element-id="' + i + '"' +
+	                                            this.getAttributesAsString(attributes) +
+	                                            ' d2-focus-next-on-enter' + 
+	                                            ' ng-model="selectedTei.' + attId + '" ' +
+	                                            ' ng-disabled="editingDisabled"' +
+	                                            ' d2-validation ' +
+	                                            ' d2-typeahead-validation ' +
+	                                            ' class="typeahead" ' +
+	                                            ' placeholder="&#xf0d7;&nbsp;&nbsp;" ' +
+	                                            ' typeahead-editable="false" ' + 
+	                                            ' typeahead="option.name as option.name for option in optionSets.' + optionSetId + '.options | filter:$viewValue | limitTo:50"' +
+	                                            ' typeahead-open-on-focus ' +
+	                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
+	                                            ' ng-required=" ' + att.mandatory + '"> ';                            
+	                        }
+	                        else if(att.valueType === "bool"){
+	                            newInputField = '<select ' +
+	                                            ' name="' + fieldName + '"' +
+	                                            ' element-id="' + i + '"' +
+	                                            this.getAttributesAsString(attributes) +
+	                                            ' d2-focus-next-on-enter' + 
+	                                            ' ng-model="selectedTei.' + attId + '" ' +
+	                                            ' ng-disabled="editingDisabled"' +
+	                                            ' ng-change="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
+	                                            ' ng-required=" ' + att.mandatory + '"> ' +
+	                                            ' <option value="">{{\'please_select\'| translate}}</option>' +
+	                                            ' <option value="false">{{\'no\'| translate}}</option>' + 
+	                                            ' <option value="true">{{\'yes\'| translate}}</option>' +
+	                                            '</select> ';
+	                        }
+	                        else if(att.valueType === "date"){
+	                            newInputField = '<input type="text" ' +
+	                                            ' name="' + fieldName + '"' +
+	                                            ' element-id="' + i + '"' +
+	                                            this.getAttributesAsString(attributes) +
+	                                            ' d2-focus-next-on-enter' + 
+	                                            ' placeholder="{{dhis2CalendarFormat.keyDateFormat}}" ' +
+	                                            ' ng-model="selectedTei.' + attId + '" ' +
+	                                            ' ng-disabled="editingDisabled"' +
+	                                            ' max-date="' + attMaxDate + '"' + '\'' +
+	                                            ' d2-date' +
+	                                            ' d2-validation ' +
+	                                            ' blur-or-change="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
+	                                            ' ng-required=" ' + att.mandatory + '"> ';
+	                        }
+	                        else if(att.valueType === "trueOnly"){
+	                            newInputField = '<input type="checkbox" ' +  
+	                                            ' name="' + fieldName + '"' +
+	                                            ' element-id="' + i + '"' +
+	                                            this.getAttributesAsString(attributes) + 
+	                                            ' d2-validation ' +
+	                                            ' d2-focus-next-on-enter' + 
+	                                            ' ng-model="selectedTei.' + attId + '" ' +
+	                                            ' ng-disabled="editingDisabled"' +
+	                                            ' ng-change="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
+	                                            ' ng-required=" ' + att.mandatory + '"> ';
+	                        }
+	                        else if(att.valueType === "email"){
+	                            newInputField = '<input type="email" ' +    
+	                                            ' name="' + fieldName + '"' +                                              
+	                                            ' element-id="' + i + '"' +
+	                                            this.getAttributesAsString(attributes) +
+	                                            ' d2-validation ' +
+	                                            ' d2-focus-next-on-enter' + 
+	                                            ' ng-model="selectedTei.' + attId + '" ' +
+	                                            ' ng-disabled="editingDisabled"' +
+	                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
+	                                            ' ng-required=" ' + att.mandatory + '"> ';
+	                        }
+	                        else {
+	                            newInputField = '<input type="text" ' +
+	                                            ' name="' + fieldName + '"' +
+	                                            ' element-id="' + i + '"' +                                             
+	                                            this.getAttributesAsString(attributes) +
+	                                            ' d2-validation ' +
+	                                            ' d2-focus-next-on-enter' + 
+	                                            ' ng-model="selectedTei.' + attId + '" ' +
+	                                            ' ng-disabled="editingDisabled"' +
+	                                            ' ng-blur="validationAndSkipLogic(selectedTei,\'' + attId + '\')" ' +
+	                                            ' ng-required=" ' + att.mandatory + '"> ';
+	                        }
+                       }
+                         
                     }                        
                  
                     if(attributes.hasOwnProperty('programid')){