diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py b/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py
index 85e452ae48330a0ca82348f8dddf3805ce34ae2f..783dd86c11c1187042cc3fa490753338f77aff1a 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/constraints/__init__.py
@@ -68,7 +68,7 @@ def filter_scheduling_units_using_constraints(scheduling_units: [models.Scheduli
 
     for scheduling_unit in scheduling_units:
         try:
-            if scheduling_unit.draft is None or scheduling_unit.draft.scheduling_constraints_template is None:
+            if scheduling_unit.draft is None or scheduling_unit.scheduling_constraints_template is None:
                 logger.warning("cannot dynamically schedule scheduling_unit id=%s name='%s' because it has not constraints template", scheduling_unit.id, scheduling_unit.name)
                 continue
 
@@ -168,7 +168,7 @@ def sort_scheduling_units_scored_by_constraints(scheduling_units: [models.Schedu
 
 def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime, upper_bound: datetime) -> bool:
     '''Check if the given scheduling_unit can run somewhere within the given time window depending on the sub's constrains-template/doc.'''
-    constraints_template = scheduling_unit.draft.scheduling_constraints_template
+    constraints_template = scheduling_unit.scheduling_constraints_template
 
     # choose appropriate method based on template (strategy pattern), or raise
     if constraints_template.name == 'constraints' and constraints_template.version == 1:
@@ -184,7 +184,7 @@ def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, l
 
 def can_run_after(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> bool:
     '''Check if the given scheduling_unit can run somewhere after the given lowerbound timestamp depending on the sub's constrains-template/doc.'''
-    constraints_template = scheduling_unit.draft.scheduling_constraints_template
+    constraints_template = scheduling_unit.scheduling_constraints_template
 
     # choose appropriate method based on template (strategy pattern), or raise
     if constraints_template.name == 'constraints' and constraints_template.version == 1:
@@ -201,7 +201,7 @@ def can_run_after(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound:
 
 def compute_scores(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound:datetime, upper_bound:datetime) -> ScoredSchedulingUnit:
     '''Compute the "fitness" scores per constraint for the given scheduling_unit at the given starttime depending on the sub's constrains-template/doc.'''
-    constraints_template = scheduling_unit.draft.scheduling_constraints_template
+    constraints_template = scheduling_unit.scheduling_constraints_template
 
     # choose appropriate method based on template (strategy pattern), or raise
     if constraints_template.name == 'constraints' and constraints_template.version == 1:
@@ -217,7 +217,7 @@ def compute_scores(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound:
 
 def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> datetime:
     '''determine the earliest possible start_time for the given scheduling unit, taking into account all its constraints'''
-    constraints_template = scheduling_unit.draft.scheduling_constraints_template
+    constraints_template = scheduling_unit.scheduling_constraints_template
 
     # choose appropriate method based on template (strategy pattern), or raise
     if constraints_template.name == 'constraints' and constraints_template.version == 1:
@@ -234,7 +234,7 @@ def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBluep
 def get_min_earliest_possible_start_time(scheduling_units: [models.SchedulingUnitBlueprint], lower_bound: datetime) -> datetime:
     '''deterimine the earliest possible starttime over all given scheduling units, taking into account all their constraints'''
     try:
-        return min(get_earliest_possible_start_time(scheduling_unit, lower_bound) for scheduling_unit in scheduling_units if scheduling_unit.draft.scheduling_constraints_template is not None)
+        return min(get_earliest_possible_start_time(scheduling_unit, lower_bound) for scheduling_unit in scheduling_units if scheduling_unit.scheduling_constraints_template is not None)
     except ValueError:
         return lower_bound
 
diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
index 594c088ecd651b9b9e7982df30a9e88b81526903..342b727554e0c3a5ca3212ab4008f8ecd116e752 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
@@ -33,7 +33,7 @@ from astropy.coordinates import Angle
 import astropy.units
 
 from lofar.sas.tmss.tmss.tmssapp import models
-from lofar.sas.tmss.tmss.tmssapp.conversions import create_astroplan_observer_for_station, Time, timestamps_and_stations_to_sun_rise_and_set, coordinates_and_timestamps_to_separation_from_bodies, coordinates_timestamps_and_stations_to_target_rise_and_set
+from lofar.sas.tmss.tmss.tmssapp.conversions import create_astroplan_observer_for_station, Time, timestamps_and_stations_to_sun_rise_and_set, coordinates_and_timestamps_to_separation_from_bodies, coordinates_timestamps_and_stations_to_target_rise_and_set, coordinates_timestamps_and_stations_to_target_transit, local_sidereal_time_for_utc_and_station
 from lofar.sas.tmss.tmss.exceptions import TMSSException
 
 from . import ScoredSchedulingUnit
@@ -61,7 +61,7 @@ def can_run_within_timewindow(scheduling_unit: models.SchedulingUnitBlueprint, l
 
 def can_run_after(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> bool:
     '''Check if the given scheduling_unit can run somewhere after the given lowerbound timestamp depending on the sub's constrains-template/doc.'''
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
     if 'before' in constraints['time']:
         before = parser.parse(constraints['time']['before'], ignoretz=True)
         return before > lower_bound
@@ -74,7 +74,7 @@ __all__ = ['can_run_within_timewindow', 'can_run_after']
 
 def has_manual_scheduler_constraint(scheduling_unit: models.SchedulingUnitBlueprint) -> bool:
     '''evaluate the scheduler contraint. Should this unit be manually scheduled?'''
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
     return constraints.get('scheduler', '') == 'manual'
 
 
@@ -101,7 +101,7 @@ def can_run_anywhere_within_timewindow_with_daily_constraints(scheduling_unit: m
     :return: True if all daily constraints are met over the entire time window, else False.
     """
     main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit)
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
     if constraints['daily']['require_day'] or constraints['daily']['require_night'] or constraints['daily']['avoid_twilight']:
 
         if (upper_bound - lower_bound).days >= 1:
@@ -158,7 +158,7 @@ def can_run_within_timewindow_with_time_constraints(scheduling_unit: models.Sche
              constraints are met over the runtime of the observation, else False.
     """
     main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit)
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
 
     # Check the 'at' constraint and then only check can_run_anywhere for the single possible time window
     if 'at' in constraints['time']:
@@ -189,7 +189,7 @@ def can_run_anywhere_within_timewindow_with_time_constraints(scheduling_unit: mo
     can_run_with_after = True
     can_run_between = True
     can_run_not_between = True
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
 
     # given time window needs to end before constraint
     if 'before' in constraints['time']:
@@ -254,7 +254,7 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod
     # TODO: remove this shortcut after demo
     return True
 
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
     if not "sky" in constraints:
         return True
 
@@ -265,7 +265,6 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod
                 angle1 = beam['angle1']
                 angle2 = beam['angle2']
                 direction_type = beam['direction_type']
-
                 if 'min_distance' in constraints['sky']:
                     # currently we only check at bounds, we probably want to add some more samples in between later on
                     distances = coordinates_and_timestamps_to_separation_from_bodies(angle1=angle1, angle2=angle2, direction_type=direction_type, timestamps=(lower_bound, upper_bound), bodies=tuple(constraints['sky']['min_distance'].keys()))
@@ -299,6 +298,81 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod
                                 else:
                                     logger.info('min_target_elevation=%s constraint is not met at timestamp=%s' % (min_elevation.rad, timestamps[i]))
                                 return False
+                if 'transit_offset' in constraints['sky'] and 'from' in constraints['sky']['transit_offset'] and task['specifications_template'] == 'target observation':
+                    # Check constraint on tile beam for HBA only:
+                    if task['specifications_doc']['antenna_set'].startswith('HBA'):
+                        # since the constraint only applies to the middle of the obs, consider its duration
+                        if 'duration' in task['specifications_doc']:
+                            duration = timedelta(seconds=task['specifications_doc']['duration'])
+                            timestamps = (lower_bound + 0.5 * duration, upper_bound - 0.5 * duration)
+                        else:
+                            timestamps = (lower_bound, upper_bound)
+                        station_groups = task['specifications_doc']['station_groups']
+                        stations = list(set(sum([group['stations'] for group in station_groups], [])))  # flatten all station_groups to single list
+                        transit_times = coordinates_timestamps_and_stations_to_target_transit(angle1=angle1, angle2=angle2, direction_type=direction_type, timestamps=timestamps, stations=tuple(stations))
+                        for station, times in transit_times.items():
+                            for i in range(len(timestamps)):
+                                offset = (timestamps[i] - times[i]).total_seconds()
+                                offset_from = constraints['sky']['transit_offset']['from']
+                                offset_to = constraints['sky']['transit_offset']['to']
+                                # because the constraint allows specifying a window that reaches past 12h from transit,
+                                # the transit that it refers to may not be the nearest transit to the observation time.
+                                # Hence we also check if the constraint is met with 24h shift (which is approximately
+                                # equivalent to checking the constraint for the previous or next transit)
+                                if not ((offset_from < offset < offset_to) or
+                                        (offset_from+86400 < offset < offset_to+86400) or
+                                        (offset_from-86400 < offset < offset_to-86400)):
+                                    logger.info('transit_offset constraint from=%s to=%s is not met by offset=%s at timestamp=%s' % (offset_from, offset_to, offset, timestamps[i]))
+                                    return False
+
+            if 'SAPs' in task['specifications_doc']:
+                if 'transit_offset' in constraints['sky']  and 'from' in constraints['sky']['transit_offset'] and task['specifications_template'] == 'target observation':
+                    # Check constraint on SAPs for LBA only:
+                    if task['specifications_doc']['antenna_set'].startswith('LBA'):
+                        # since the constraint only applies to the middle of the obs, consider its duration
+                        if 'duration' in task['specifications_doc']:
+                            duration = timedelta(seconds=task['specifications_doc']['duration'])
+                            timestamps = (lower_bound + 0.5 * duration, upper_bound - 0.5 * duration)
+                        else:
+                            timestamps = (lower_bound, upper_bound)
+
+                        # for LBA get transit times for all SAPs...
+                        sap_transit_times = []
+                        station_groups = task['specifications_doc']['station_groups']
+                        stations = list(set(sum([group['stations'] for group in station_groups], [])))  # flatten all station_groups to single list
+                        for sap in task['specifications_doc']['SAPs']:
+                            angle1 = sap['digital_pointing']['angle1']
+                            angle2 = sap['digital_pointing']['angle2']
+                            direction_type = sap['digital_pointing']['direction_type']
+                            sap_transit_times.append(coordinates_timestamps_and_stations_to_target_transit(angle1=angle1, angle2=angle2, direction_type=direction_type, timestamps=timestamps, stations=tuple(stations)))
+
+                        # ...then for each station and timestamp, average the transit times we got for the different SAPs
+                        transit_times = {}
+                        _reference_date = datetime(1900, 1, 1)
+                        for station in stations:
+                            for j in range(len(timestamps)):
+                                sap_datetime_list = [sap_transit_times[i][station][j] for i in range(len(task['specifications_doc']['SAPs']))]
+                                average_transit_time = _reference_date + sum([date - _reference_date for date in sap_datetime_list], timedelta()) / len(sap_datetime_list)
+                                transit_times.get(station, []).append(average_transit_time)
+
+                        logger.warning('##### %s' % transit_times)
+
+                        for station, times in transit_times.items():
+                            for i in range(len(timestamps)):
+                                offset = (timestamps[i] - times[i]).total_seconds()
+                                offset_from = constraints['sky']['transit_offset']['from']
+                                offset_to = constraints['sky']['transit_offset']['to']
+                                # because the constraint allows specifying a window that reaches past 12h from transit,
+                                # the transit that it refers to may not be the nearest transit to the observation time.
+                                # Hence we also check if the constraint is met with 24h shift (which is approximately
+                                # equivalent to checking the constraint for the previous or next transit)
+                                if not ((offset_from < offset < offset_to) or
+                                        (offset_from+86400 < offset < offset_to+86400) or
+                                        (offset_from-86400 < offset < offset_to-86400)):
+                                    logger.info('transit_offset constraint from=%s to=%s is not met by offset=%s at timestamp=%s' % (offset_from, offset_to, offset, timestamps[i]))
+                                    return False
+
+
 
     return True
 
@@ -312,7 +386,7 @@ def get_target_observation_task_name_from_requirements_doc(scheduling_unit: mode
 
 
 def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound: datetime) -> datetime:
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
 
     main_observation_task_name = get_target_observation_task_name_from_requirements_doc(scheduling_unit)
     duration = timedelta(seconds=scheduling_unit.requirements_doc['tasks'][main_observation_task_name]['specifications_doc']['duration'])
@@ -383,7 +457,7 @@ def get_earliest_possible_start_time(scheduling_unit: models.SchedulingUnitBluep
 
 def compute_scores(scheduling_unit: models.SchedulingUnitBlueprint, lower_bound:datetime, upper_bound:datetime) -> ScoredSchedulingUnit:
     '''Compute the "fitness" scores per constraint for the given scheduling_unit at the given starttime depending on the sub's constrains-template/doc.'''
-    constraints = scheduling_unit.draft.scheduling_constraints_doc
+    constraints = scheduling_unit.scheduling_constraints_doc
 
     # TODO: add compute_scores methods for each type of constraint
     # TODO: take start_time into account. For example, an LST constraint yields a better score when the starttime is such that the center of the obs is at LST.
diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
index 3b45ac16bd908ccd1a845b0b63876b4c2039b073..5ff4971b7f719615583eaf50ad3aaf5b86d27f92 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
@@ -298,8 +298,7 @@ def get_dynamically_schedulable_scheduling_units() -> [models.SchedulingUnitBlue
     defined_independend_subtasks = models.Subtask.independent_subtasks().filter(state__value='defined')
     defined_independend_subtask_ids = defined_independend_subtasks.values('task_blueprints__scheduling_unit_blueprint_id').distinct().all()
     scheduling_units = models.SchedulingUnitBlueprint.objects.filter(id__in=defined_independend_subtask_ids) \
-                                                             .filter(draft__scheduling_constraints_template__isnull=False) \
-                                                             .select_related('draft', 'draft__scheduling_constraints_template').all()
+                                                             .filter(scheduling_constraints_template__isnull=False).all()
     return [su for su in scheduling_units if su.status == 'schedulable']
 
 
diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
index 82bd9243e1897bd246367eb96ebb97f88dc927a5..59e644b4a882c4add00baa7b495fb95f41a524df 100755
--- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
@@ -426,7 +426,7 @@ class TestDailyConstraints(TestCase):
     # require_day
 
     def test_get_earliest_possible_start_time_with_daytime_constraint_returns_day_start(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
         self.sunrise_mock.return_value = self.sunrise_data_early_night
         timestamp = datetime(2020, 1, 1, 4, 0, 0)
@@ -435,7 +435,7 @@ class TestDailyConstraints(TestCase):
 
     def test_get_earliest_possible_start_time_with_daytime_constraint_returns_day_start_of_latest_station(self):
         self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['station_groups'] = [{'stations': ['CS001', 'DE601']}]
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
         self.sunrise_mock.return_value = self.sunrise_data_early_night
         timestamp = datetime(2020, 1, 1, 4, 0, 0)
@@ -443,28 +443,28 @@ class TestDailyConstraints(TestCase):
         self.assertEqual(returned_time, self.sunrise_data['DE601']['day'][0]['start'])
 
     def test_get_earliest_possible_start_time_with_daytime_constraint_returns_timestamp(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 10, 0, 0)
         returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
         self.assertEqual(returned_time, timestamp)
 
     def test_get_earliest_possible_start_time_with_daytime_constraint_returns_next_day_start(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 20, 0, 0)
         returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
         self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][1]['start'])
 
     def test_get_earliest_possible_start_time_with_daytime_constraint_returns_next_day_start_when_obs_does_not_fit(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 14, 0, 0)
         returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
         self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][1]['start'])
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_true(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
@@ -473,7 +473,7 @@ class TestDailyConstraints(TestCase):
         self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound))
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_false_when_not_daytime(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
@@ -482,7 +482,7 @@ class TestDailyConstraints(TestCase):
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound))
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_daytime_constraint_returns_false_when_partially_not_daytime(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
@@ -498,10 +498,10 @@ class TestDailyConstraints(TestCase):
     def test_can_run_within_timewindow_with_daytime_constraint_returns_correct_value(self):
         # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked
         # remove other constraints:
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {}
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {}
 
         # set constraint to test
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_day'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_day'] = True
         self.scheduling_unit_blueprint.save()
 
         # can run in day
@@ -519,7 +519,7 @@ class TestDailyConstraints(TestCase):
     # require_night
 
     def test_get_earliest_possible_start_time_with_nighttime_constraint_returns_night_start(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 14, 0, 0)
         returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
@@ -527,14 +527,14 @@ class TestDailyConstraints(TestCase):
 
     def test_get_earliest_possible_start_time_with_nighttime_constraint_returns_night_start_of_latest_station(self):
         self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['station_groups'] = [{'stations': ['CS001', 'DE601']}]
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 14, 0, 0)
         returned_time = get_earliest_possible_start_time(self.scheduling_unit_blueprint, timestamp)
         self.assertEqual(returned_time, self.sunrise_data['DE601']['night'][0]['start'])
 
     def test_get_earliest_possible_start_time_with_nighttime_constraint_returns_timestamp(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
 
         # late night
@@ -549,7 +549,7 @@ class TestDailyConstraints(TestCase):
         self.assertEqual(returned_time, timestamp)
 
     def test_get_earliest_possible_start_time_with_nighttime_constraint_returns_next_night_start_when_obs_does_not_fit(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
 
         # early night
@@ -559,7 +559,7 @@ class TestDailyConstraints(TestCase):
         self.assertEqual(returned_time, self.sunrise_data_early_night['CS001']['night'][1]['start'])
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_true(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
 
         # early night
@@ -581,7 +581,7 @@ class TestDailyConstraints(TestCase):
         self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound))
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_false_when_not_nighttime(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
@@ -590,7 +590,7 @@ class TestDailyConstraints(TestCase):
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound))
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_nighttime_constraint_returns_false_when_partially_not_nighttime(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
 
         # night-day next day
@@ -632,10 +632,10 @@ class TestDailyConstraints(TestCase):
     def test_can_run_within_timewindow_with_nighttime_constraint_returns_correct_value(self):
         # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked
         # remove other constraints:
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {}
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {}
 
         # set constraint to test
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['require_night'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['require_night'] = True
         self.scheduling_unit_blueprint.save()
 
         # cannot run in day
@@ -654,7 +654,7 @@ class TestDailyConstraints(TestCase):
     # avoid_twilight
 
     def test_get_earliest_possible_start_time_with_twilight_constraint_returns_day_start(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_early_night
@@ -664,7 +664,7 @@ class TestDailyConstraints(TestCase):
 
     def test_get_earliest_possible_start_time_with_twilight_constraint_returns_day_start_of_latest_station(self):
         self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['station_groups'] = [{'stations': ['CS001', 'DE601']}]
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_early_night
@@ -673,7 +673,7 @@ class TestDailyConstraints(TestCase):
         self.assertEqual(returned_time, self.sunrise_data['DE601']['day'][0]['start'])
 
     def test_get_earliest_possible_start_time_with_twilight_constraint_returns_night_start(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data
@@ -683,7 +683,7 @@ class TestDailyConstraints(TestCase):
 
     def test_get_earliest_possible_start_time_with_twilight_constraint_returns_night_start_of_latest_station(self):
         self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['station_groups'] = [{'stations': ['CS001', 'DE601']}]
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data
@@ -692,7 +692,7 @@ class TestDailyConstraints(TestCase):
         self.assertEqual(returned_time, self.sunrise_data['DE601']['night'][0]['start'])
 
     def test_get_earliest_possible_start_time_with_twilight_constraint_returns_timestamp(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         # daytime
@@ -712,7 +712,7 @@ class TestDailyConstraints(TestCase):
         self.assertEqual(returned_time, timestamp)
 
     def test_get_earliest_possible_start_time_with_twilight_constraint_returns_day_or_night_start_when_obs_does_not_fit(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         timestamp = datetime(2020, 1, 1, 15, 0, 0)
@@ -725,7 +725,7 @@ class TestDailyConstraints(TestCase):
         self.assertEqual(returned_time, self.sunrise_data['CS001']['day'][0]['start'])
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_true(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
@@ -734,7 +734,7 @@ class TestDailyConstraints(TestCase):
         self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound))
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_false_when_in_twilight(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
@@ -748,7 +748,7 @@ class TestDailyConstraints(TestCase):
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_daily_constraints(self.scheduling_unit_blueprint, lower_bound, upper_bound))
 
     def test_can_run_anywhere_within_timewindow_with_daily_constraints_with_twilight_constraint_returns_false_when_partially_in_twilight(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         self.sunrise_mock.return_value = self.sunrise_data_late_night_late_night
@@ -764,10 +764,10 @@ class TestDailyConstraints(TestCase):
     def test_can_run_within_timewindow_with_twilight_constraint_returns_correct_value(self):
         # todo: for time ranges across dates, consider removing the mock for this because the moving window cannot be easily mocked
         # remove other constraints:
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {}
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {}
 
         # set constraint to test
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['daily']['avoid_twilight'] = True
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['daily']['avoid_twilight'] = True
         self.scheduling_unit_blueprint.save()
 
         # can run in day
@@ -812,23 +812,30 @@ class TestSkyConstraints(unittest.TestCase):
                                                    {"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0), "always_above_horizon": False, "always_below_horizon": False}]}
         self.target_rise_and_set_data_always_above = {"CS002": [{"rise": None, "set": None, "always_above_horizon": True, "always_below_horizon": False}]}
         self.target_rise_and_set_data_always_below = {"CS002": [{"rise": None, "set": None, "always_above_horizon": False, "always_below_horizon": True}]}
-
         self.target_rise_and_set_patcher = mock.patch('lofar.sas.tmss.services.scheduling.constraints.template_constraints_v1.coordinates_timestamps_and_stations_to_target_rise_and_set')
         self.target_rise_and_set_mock = self.target_rise_and_set_patcher.start()
         self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data
         self.addCleanup(self.target_rise_and_set_patcher.stop)
 
+        self.target_transit_data = {"CS002": [datetime(2020, 1, 1, 14, 0, 0), datetime(2020, 1, 1, 14, 0, 0)]}
+        self.target_transit_data_previous = {"CS002": [datetime(2019, 12, 31, 14, 0, 0), datetime(2020, 1, 1, 14, 0, 0)]}
+        self.target_transit_data_saps = [{"CS001": [datetime(2020, 1, 1, 14, 0, 0), datetime(2020, 1, 1, 14, 0, 0)]}, {"CS001": [datetime(2020, 1, 1, 16, 0, 0), datetime(2020, 1, 1, 16, 0, 0)]}]
+        self.target_transit_patcher = mock.patch('lofar.sas.tmss.services.scheduling.constraints.template_constraints_v1.coordinates_timestamps_and_stations_to_target_transit')
+        self.target_transit_mock = self.target_transit_patcher.start()
+        self.target_transit_mock.return_value = self.target_transit_data
+        self.addCleanup(self.target_transit_patcher.stop)
+
     # min_distance
 
     def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_distance_constraint_returns_true_when_met(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_distance': {'sun': 0.1, 'moon': 0.1, 'jupiter': 0.1}}
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'min_distance': {'sun': 0.1, 'moon': 0.1, 'jupiter': 0.1}}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 10, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
         self.assertTrue(returned_value)
 
     def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_distance_constraint_returns_false_when_not_met(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_distance': {'sun': 0.2, 'moon': 0.2, 'jupiter': 0.2}}
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'min_distance': {'sun': 0.2, 'moon': 0.2, 'jupiter': 0.2}}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 10, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
@@ -837,19 +844,83 @@ class TestSkyConstraints(unittest.TestCase):
     # min_target_elevation
 
     def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_true_when_met(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1}
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 10, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
         self.assertTrue(returned_value)
 
     def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_false_when_not_met(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.2}
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.2}
         self.scheduling_unit_blueprint.save()
         timestamp = datetime(2020, 1, 1, 11, 0, 0)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
         self.assertFalse(returned_value)
 
+    # transit_offset
+
+    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_transit_offset_constraint_returns_true_when_met(self):
+        # case 1: transits at 14h, obs middle is at 13h, so we have an offset of -3600 seconds
+
+        # big window
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43200, 'to': 43200}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 12, 0, 0)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertTrue(returned_value)
+
+        # narrow window
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -3601, 'to': -3599}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 12, 0, 0)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertTrue(returned_value)
+
+        # case 2: transits at 14h, obs middle is at 2h, so we have an offset of -43200 seconds
+
+        # window spans past 12h, so reference transit is not nearest transit to obs time
+        self.target_transit_mock.return_value = self.target_transit_data_previous
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43300, 'to': -43100}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 1, 0, 0)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertTrue(returned_value)
+        self.target_transit_mock.return_value = self.target_transit_data
+
+    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_transit_offset_constraint_returns_false_when_not_met(self):
+        # transits at 14h, obs middle is at 13h, so we have an offset of -3600 seconds
+
+        # window after
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -3599, 'to': 43200}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 12, 0, 0)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertFalse(returned_value)
+
+        # window before
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -43200, 'to': -3601}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 12, 0, 0)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertFalse(returned_value)
+
+
+    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_transit_offset_constraint_averages_SAPs_for_LBA(self):
+        # sap1 transits at 14h, sap2 transits at 16h, so average transit is at 15h
+        # obs middle is 13h, so we have an offset of -7200 seconds
+
+        self.target_transit_mock.side_effect = self.target_transit_data_saps
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'transit_offset': {'from': -7201, 'to': -7199}}  # todo: use blueprint contraints after TMSS-697 was merged
+        self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['antenna_set'] = 'LBA_INNER'
+        self.scheduling_unit_blueprint.requirements_doc['tasks']['Observation']['specifications_doc']['SAPs'] = \
+            [{'name': 'CygA', 'target': 'CygA', 'subbands': [0, 1], 'digital_pointing': {'angle1': 5.233660650313663, 'angle2': 0.7109404782526458, 'direction_type': 'J2000'}},
+             {'name': 'CasA', 'target': 'CasA', 'subbands': [2, 3], 'digital_pointing': {'angle1': 6.233660650313663, 'angle2': 0.6109404782526458, 'direction_type': 'J2000'}}]
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 12, 0, 0)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertTrue(returned_value)
+        self.target_transit_mock.side_effect = None
+
 
 class TestTimeConstraints(TestCase):
     """
@@ -863,28 +934,28 @@ class TestTimeConstraints(TestCase):
     """
 
     def add_time_at_constraint(self, at_timestamp):
-        lst_at_constraint = self.scheduling_unit_blueprint.draft.scheduling_constraints_doc
+        lst_at_constraint = self.scheduling_unit_blueprint.scheduling_constraints_doc
         lst_at_constraint['time']['at'] = at_timestamp.isoformat()
         self.scheduling_unit_blueprint.save()
 
     def add_time_between_constraint(self, from_timestamp, to_timestamp):
-        lst_between_constraints = self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["between"]
+        lst_between_constraints = self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["between"]
         time_constraint_dict = {"from": from_timestamp.isoformat(), "to": to_timestamp.isoformat()}
         lst_between_constraints.append(time_constraint_dict)
         self.scheduling_unit_blueprint.save()
 
     def add_time_not_between_constraint(self, from_timestamp, to_timestamp):
-        lst_between_constraints = self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["not_between"]
+        lst_between_constraints = self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["not_between"]
         time_constraint_dict = {"from": from_timestamp.isoformat(), "to": to_timestamp.isoformat()}
         lst_between_constraints.append(time_constraint_dict)
         self.scheduling_unit_blueprint.save()
 
     def clear_time_constraints(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["between"] = []
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["not_between"] = []
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time'].pop('at', None)
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time'].pop("before", None)
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time'].pop('after', None)
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["between"] = []
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["not_between"] = []
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time'].pop('at', None)
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time'].pop("before", None)
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time'].pop('after', None)
 
     def setUp(self) -> None:
         # scheduling unit
@@ -902,7 +973,7 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints before lower_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
         self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
@@ -911,28 +982,28 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints equal to lower_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
 
         # Set datetime constraints after lower_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                              datetime(2020, 1, 1, 12, 0, 0),
                                                                              datetime(2020, 1, 2, 12, 0, 0)))
 
         # Set datetime constraints to upper_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 12, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 12, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                              datetime(2020, 1, 1, 12, 0, 0),
                                                                              datetime(2020, 1, 2, 12, 0, 0)))
 
         # Set datetime constraints after upper_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                              datetime(2020, 1, 1, 12, 0, 0),
                                                                              datetime(2020, 1, 2, 12, 0, 0)))
@@ -941,14 +1012,14 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints before lower bounds, but with too short window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 1, 13, 0, 0)))
 
         # Set datetime constraints after lower bounds, and with too little space left in window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 14, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 14, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 1, 15, 0, 0)))
@@ -957,14 +1028,14 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints before lower bounds, and with sufficient window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
         self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 1, 14, 0, 0)))
 
         # Set datetime constraints after lower bounds, but with sufficient space left in window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
         self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 1, 16, 0, 0)))
@@ -975,27 +1046,27 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints before lower_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 11, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
 
         # Set datetime constraints equal to lower_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 12, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 12, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
 
         # Set datetime constraints after lower_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                              datetime(2020, 1, 1, 12, 0, 0),
                                                                              datetime(2020, 1, 2, 12, 0, 0)))
         # Set datetime constraints equal to upper_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 12, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 12, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                              datetime(2020, 1, 1, 12, 0, 0),
                                                                              datetime(2020, 1, 2, 12, 0, 0)))
@@ -1005,7 +1076,7 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints after upper_bound
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
         self.assertTrue(tc1.can_run_anywhere_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
@@ -1014,14 +1085,14 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints after upper bound, but with too short window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 2, 11, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
 
         # Set datetime constraints after lower bound, and with too little space left in window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
         self.assertFalse(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
@@ -1030,14 +1101,14 @@ class TestTimeConstraints(TestCase):
 
         # Set datetime constraints after upper bounds, and with sufficient window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 13, 0, 0).isoformat()
         self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
 
         # Set datetime constraints after lower bounds, but with sufficient space left in window for obs duration
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat()
         self.assertTrue(tc1.can_run_within_timewindow_with_time_constraints(self.scheduling_unit_blueprint,
                                                                             datetime(2020, 1, 1, 12, 0, 0),
                                                                             datetime(2020, 1, 2, 12, 0, 0)))
@@ -1318,22 +1389,22 @@ class TestTimeConstraints(TestCase):
 
         # Set before and after constraint with sufficient gap to fit observation, and assert True
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 59, 59).isoformat()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 1).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 12, 59, 59).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 1).isoformat()
         self.assertTrue(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary())
 
         # set before and after constraint with slightly smaller gap for observation, and assert False
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 1, 15, 0, 0).isoformat()
         self.assertFalse(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary())
 
         # set before and after constraint with large gap
         # then and add additional between and not between constraints until window is blocked
         # can run 13-8h
         self.clear_time_constraints()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 8, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["after"] = datetime(2020, 1, 1, 13, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 8, 0, 0).isoformat()
         self.assertTrue(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary())
 
         # can run 13h-20h
@@ -1353,7 +1424,7 @@ class TestTimeConstraints(TestCase):
         self.assertTrue(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary())
 
         # move before constraint, can not run anymore
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 5, 0, 0).isoformat()
+        self.scheduling_unit_blueprint.scheduling_constraints_doc['time']["before"] = datetime(2020, 1, 2, 5, 0, 0).isoformat()
         self.assertFalse(self.execute_can_run_within_timewindow_with_time_constraints_of_24hour_boundary())
 
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
index 14b0a38e566666fda10ba8292bb9d4f91525afef..642e7090c070be2033a4af4c8404c137bbe2b771 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
@@ -35,7 +35,6 @@ SUN_SET_RISE_ANGLE_TO_HORIZON = Angle(10, unit=astropy.units.deg)
 # TODO: To be considered, now we store the sunset/sunrise data in advanced, we can increase the number of points!!
 SUN_SET_RISE_PRECISION = 30
 
-
 def timestamps_and_stations_to_sun_rise_and_set(timestamps: tuple, stations: tuple, angle_to_horizon: Angle=SUN_SET_RISE_ANGLE_TO_HORIZON,
                                                 create_when_not_found=False) -> dict:
     """
@@ -268,6 +267,34 @@ def coordinates_timestamps_and_stations_to_target_rise_and_set(angle1: float, an
 
     return return_dict
 
+# default n_grid_points; higher is more precise but very costly; astropy defaults to 150, note that errors can be in the minutes with a lower values
+TARGET_TRANSIT_PRECISION = 150
+
+@lru_cache(maxsize=256, typed=False)  # does not like lists, so use tuples to allow caching
+def coordinates_timestamps_and_stations_to_target_transit(angle1: float, angle2: float, direction_type: str, timestamps: tuple, stations: tuple) -> dict:
+    """
+    Compute nearest meridian transit times of the given coordinates for each given station and timestamp.
+    :param angle1: first angle of celectial coordinates, e.g. RA
+    :param angle2: second angle of celectial coordinates, e.g. Dec
+    :param direction_type: direction_type of celectial coordinates, e.g. 'J2000'
+    :param timestamps: tuple of datetimes, e.g. (datetime(2020, 1, 1), datetime(2020, 1, 2))
+    :param stations: tuple of station names, e.g. ("CS002",)
+    :return A dict that maps station names to a list of transit times (nearest transit for each requested timestamp).
+        E.g.
+        {"CS002": [datetime(2020, 1, 1, 4, 0, 0), datetime(2020, 1, 2, 4, 0, 0)]}
+    """
+    if direction_type == "J2000":
+        coord = astropy.coordinates.SkyCoord(ra=angle1, dec=angle2, unit=astropy.units.rad)
+    else:
+        raise ValueError("Do not know how to convert direction_type=%s to SkyCoord" % direction_type)
+    return_dict = {}
+    for station in stations:
+        for timestamp in timestamps:
+            # todo: this can probably be made faster by moving the following logic to an own function with single station/timestamp as input and putting the lru_cache on there.
+            observer = create_astroplan_observer_for_station(station)
+            target_transit = observer.target_meridian_transit_time(target=coord, time=Time(timestamp), which='nearest', n_grid_points=TARGET_TRANSIT_PRECISION)
+            return_dict.setdefault(station, []).append(target_transit.to_datetime())
+    return return_dict
 
 
 def local_sidereal_time_for_utc_and_station(timestamp: datetime = None,
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py
index e2cb08e37434b494c8b502cde395f9ef10510dcb..0110ee6adaa831b503f1b8b42ea28f0ed7a6d0d2 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.0.9 on 2021-04-08 14:57
+# Generated by Django 3.0.9 on 2021-04-28 21:14
 
 from django.conf import settings
 import django.contrib.postgres.fields
@@ -617,9 +617,10 @@ class Migration(migrations.Migration):
                 ('ingest_permission_granted_since', models.DateTimeField(help_text='The moment when ingest permission was granted.', null=True)),
                 ('output_pinned', models.BooleanField(default=False, help_text='boolean (default FALSE), which blocks deleting unpinned dataproducts. When toggled ON, backend must pick SUB up for deletion. It also must when dataproducts are unpinned.')),
                 ('results_accepted', models.BooleanField(default=False, help_text='boolean (default NULL), which records whether the results were accepted, allowing the higher-level accounting to be adjusted.')),
-                ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')),
                 ('piggyback_allowed_tbb', models.BooleanField(help_text='Piggyback key for TBB.', null=True)),
                 ('piggyback_allowed_aartfaac', models.BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)),
+                ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')),
+                ('scheduling_constraints_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling Constraints for this run.', null=True)),
             ],
             options={
                 'abstract': False,
@@ -639,9 +640,9 @@ class Migration(migrations.Migration):
                 ('generator_instance_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Parameter value that generated this run draft (NULLable).', null=True)),
                 ('scheduling_constraints_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling Constraints for this run.', null=True)),
                 ('ingest_permission_required', models.BooleanField(default=False, help_text='Explicit permission is needed before the task.')),
-                ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')),
                 ('piggyback_allowed_tbb', models.BooleanField(help_text='Piggyback key for TBB.', null=True)),
                 ('piggyback_allowed_aartfaac', models.BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)),
+                ('priority_rank', models.FloatField(default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')),
             ],
             options={
                 'abstract': False,
@@ -1251,6 +1252,11 @@ class Migration(migrations.Migration):
             name='requirements_template',
             field=models.ForeignKey(help_text='Schema used for requirements_doc (IMMUTABLE).', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.SchedulingUnitTemplate'),
         ),
+        migrations.AddField(
+            model_name='schedulingunitblueprint',
+            name='scheduling_constraints_template',
+            field=models.ForeignKey(help_text='Schema used for scheduling_constraints_doc.', null=True, on_delete=django.db.models.deletion.CASCADE, to='tmssapp.SchedulingConstraintsTemplate'),
+        ),
         migrations.AddField(
             model_name='schedulingset',
             name='generator_source',
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
index b927f609a143033d3169b34b1d4a30bcd7bb3360..ada071a865bdf4f336164fa504e62eb9f7083a87 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
@@ -17,7 +17,7 @@ from django.core.exceptions import ValidationError
 import datetime
 from collections import Counter
 from django.utils.functional import cached_property
-
+from lofar.sas.tmss.tmss.exceptions import TMSSException
 
 #
 # Mixins
@@ -480,6 +480,8 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem
         SCHEDULED = "scheduled"
         SCHEDULABLE = "schedulable"
 
+    # todo: are many of these fields supposed to be immutable in the database?
+    #  Or are we fine to just not allow most users to change them?
     requirements_doc = JSONField(help_text='Scheduling and/or quality requirements for this scheduling unit (IMMUTABLE).')
     do_cancel = BooleanField()
     ingest_permission_required = BooleanField(default=False, help_text='Explicit permission is needed before the task.')
@@ -492,19 +494,40 @@ class SchedulingUnitBlueprint(RefreshFromDbInvalidatesCachedPropertiesMixin, Tem
     piggyback_allowed_aartfaac = BooleanField(help_text='Piggyback key for AARTFAAC.', null=True)
     priority_rank = FloatField(null=False, default=0.0, help_text='Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.')
     priority_queue = ForeignKey('PriorityQueueType', null=False, on_delete=PROTECT, default="A", help_text='Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units.')
+    scheduling_constraints_doc = JSONField(help_text='Scheduling Constraints for this run.', null=True)
+    scheduling_constraints_template = ForeignKey('SchedulingConstraintsTemplate', on_delete=CASCADE, null=True, help_text='Schema used for scheduling_constraints_doc.')
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # keep original scheduling constraints to detect changes on save
+        # Note: we cannot use self.scheduling_constraints_doc here since that causes an infinite loop of update_from_db
+        if 'scheduling_constraints_doc' in kwargs.keys():
+            self.__original_scheduling_constraints_doc = kwargs['scheduling_constraints_doc']
+        else:
+            self.__original_scheduling_constraints_doc = None
+        self.__original_scheduling_constraints_template_id = self.scheduling_constraints_template_id
 
     def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
         self.annotate_validate_add_defaults_to_doc_using_template('requirements_doc', 'requirements_template')
-       
-        # This code only happens if the objects is not in the database yet. self._state.adding is True creating
-        if self._state.adding and hasattr(self, 'draft'):
-            self.ingest_permission_required = self.draft.ingest_permission_required
-
-        # Propagate scheduling_unit_draft piggyback values as default for scheduling_unit_blueprint
-        if self._state.adding and self.piggyback_allowed_tbb is None and hasattr(self, 'draft'):
-            self.piggyback_allowed_tbb = self.draft.piggyback_allowed_tbb
-        if self._state.adding and self.piggyback_allowed_aartfaac is None and hasattr(self, 'draft'):
-            self.piggyback_allowed_aartfaac = self.draft.piggyback_allowed_aartfaac
+
+        if self._state.adding:
+            # On creation, propagate the following scheduling_unit_draft attributes as default for the new scheduling_unit_blueprint
+            for copy_field in ['ingest_permission_required', 'piggyback_allowed_tbb', 'piggyback_allowed_aartfaac',
+                               'scheduling_constraints_doc', 'scheduling_constraints_template']:
+                if hasattr(self, 'draft'):
+                    setattr(self, copy_field, getattr(self.draft, copy_field))
+        else:
+            # On updates, prevent changing the scheduling constraints doc or template if we are past schedulable state
+            # todo: This causes a ton of tests to fail, e.g. t_workflow_qaworkflow returns errors 422
+            if self.status not in [SchedulingUnitBlueprint.Status.DEFINED.value, SchedulingUnitBlueprint.Status.SCHEDULABLE.value] and \
+                    ((self.__original_scheduling_constraints_doc is not None and self.scheduling_constraints_doc != self.__original_scheduling_constraints_doc) or
+                     self.scheduling_constraints_template_id != self.__original_scheduling_constraints_template_id):
+                raise TMSSException('The scheduling constraints of SchedulingUnitBlueprint pk=%s status=%s cannot be updated since it is not in defined or schedulable state.' % (self.pk, self.status))
+
+        # update the original constraints value for comparison on next save
+        self.__original_scheduling_constraints_doc = self.scheduling_constraints_doc
+        self.__original_scheduling_constraints_template_id = self.scheduling_constraints_template_id
 
         super().save(force_insert, force_update, using, update_fields)
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/views.py b/SAS/TMSS/backend/src/tmss/tmssapp/views.py
index 85bdfe0de03a90428f85f01fb51264e4b4082b49..c043399964b788b809194e49c1c0b6872e57fdfe 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/views.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/views.py
@@ -18,7 +18,7 @@ from datetime import datetime
 import dateutil.parser
 from astropy.coordinates import Angle
 import astropy.units
-from lofar.sas.tmss.tmss.tmssapp.conversions import local_sidereal_time_for_utc_and_station, local_sidereal_time_for_utc_and_longitude, timestamps_and_stations_to_sun_rise_and_set, coordinates_and_timestamps_to_separation_from_bodies, coordinates_timestamps_and_stations_to_target_rise_and_set
+from lofar.sas.tmss.tmss.tmssapp.conversions import local_sidereal_time_for_utc_and_station, local_sidereal_time_for_utc_and_longitude, timestamps_and_stations_to_sun_rise_and_set, coordinates_and_timestamps_to_separation_from_bodies, coordinates_timestamps_and_stations_to_target_rise_and_set, coordinates_timestamps_and_stations_to_target_transit
 
 # Note: Decorate with @api_view to get this picked up by Swagger
 
@@ -278,3 +278,27 @@ def get_target_rise_and_set(request):
     rise_set_dict = coordinates_timestamps_and_stations_to_target_rise_and_set(angle1=angle1, angle2=angle2, direction_type=direction_type, angle_to_horizon=horizon, timestamps=timestamps, stations=stations)
     return JsonResponse(rise_set_dict)
 
+
+@api_view(['GET'])
+def get_target_transit(request):
+    '''
+    returns transit times of the given coordinates for each given station and timestamp.
+    '''
+    timestamps = request.GET.get('timestamps', None)
+    angle1 = request.GET.get('angle1')
+    angle2 = request.GET.get('angle2')
+    direction_type = request.GET.get("direction_type", "J2000")
+    stations = tuple(request.GET.get('stations', "CS002").split(','))
+
+    if angle1 is None or angle2 is None:
+        raise ValueError("Please provide celestial coordinates via 'angle1', 'angle2' (and optionally 'direction_type') properties.")
+
+    if timestamps is None:
+        timestamps = (datetime.utcnow(),)
+    else:
+        timestamps = timestamps.split(',')
+        timestamps = tuple([dateutil.parser.parse(timestamp, ignoretz=True) for timestamp in timestamps])  #  isot to datetime
+
+    # calculate
+    transit_dict = coordinates_timestamps_and_stations_to_target_transit(angle1=angle1, angle2=angle2, direction_type=direction_type, timestamps=timestamps, stations=stations)
+    return JsonResponse(transit_dict)
diff --git a/SAS/TMSS/backend/src/tmss/urls.py b/SAS/TMSS/backend/src/tmss/urls.py
index 5306787cb405fa524cbb475cc7d7e76d1fe3c561..c077e51431b29da1484c0653421d54c27a7a5f91 100644
--- a/SAS/TMSS/backend/src/tmss/urls.py
+++ b/SAS/TMSS/backend/src/tmss/urls.py
@@ -75,6 +75,7 @@ urlpatterns = [
     re_path('util/lst/?', views.lst, name="conversion-lst"),
     re_path('util/angular_separation/?', views.get_angular_separation, name='get_angular_separation'),
     re_path('util/target_rise_and_set/?', views.get_target_rise_and_set, name='get_target_rise_and_set'),
+    re_path('util/target_transit/?', views.get_target_transit, name='get_target_transit'),
 ]
 
 if os.environ.get('SHOW_DJANGO_DEBUG_TOOLBAR', False):
diff --git a/SAS/TMSS/backend/test/t_conversions.py b/SAS/TMSS/backend/test/t_conversions.py
index 6a07693cbced93562963ebd79790cf1716c58e0e..942fb172dd634dd99f95732459ac833b0e37a622 100755
--- a/SAS/TMSS/backend/test/t_conversions.py
+++ b/SAS/TMSS/backend/test/t_conversions.py
@@ -288,11 +288,12 @@ class UtilREST(unittest.TestCase):
         # defaults are CS002 and today
         self.assertIn('CS002', r_dict.keys())
 
-        # assert day of timestamp matches day of returned rise
-        expected_date = datetime.date.today()
+        # assert target sets within 24h after now and rises within 24h before it sets
+        expected_date = datetime.datetime.utcnow()
         target_rise = dateutil.parser.parse(r_dict['CS002'][0]['rise'])
         target_set = dateutil.parser.parse(r_dict['CS002'][0]['set'])
-        self.assertTrue(expected_date == target_rise.date() or expected_date == target_set.date())
+        self.assertTrue(0 < (target_set - expected_date).total_seconds() < 86400)
+        self.assertTrue(0 < (target_set - target_rise).total_seconds() < 86400)
 
     def test_util_target_rise_and_set_considers_stations(self):
         stations = ['CS005', 'RS305', 'DE609']
@@ -353,13 +354,13 @@ class UtilREST(unittest.TestCase):
 
     def test_util_target_rise_and_set_considers_horizon(self):
         test_horizons = [0.1, 0.2, 0.3]
+        rise_last = None
         for horizon in test_horizons:
             r = requests.get(BASE_URL + '/util/target_rise_and_set?angle1=0.5&angle2=0.5&horizon=%s' % horizon, auth=AUTH)
             self.assertEqual(r.status_code, 200)
             r_dict = json.loads(r.content.decode('utf-8'))
 
             # assert all requested horizons yield a response and times differ
-            rise_last = None
             rise = r_dict['CS002'][0]['rise']
             if rise_last:
                 self.assertNotEqual(rise, rise_last)
@@ -394,6 +395,76 @@ class UtilREST(unittest.TestCase):
         self.assertFalse(r_dict['CS002'][0]['always_above_horizon'])
         self.assertTrue(r_dict['CS002'][0]['always_below_horizon'])
 
+    # target transit
+
+    def test_util_target_transit_returns_json_structure_with_defaults(self):
+        r = requests.get(BASE_URL + '/util/target_transit?angle1=0.5&angle2=0.5', auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # defaults are CS002 and today
+        self.assertIn('CS002', r_dict.keys())
+
+        # assert returned timestamp is no further than 12h away from now
+        expected_time = datetime.datetime.utcnow()
+        returned_time = dateutil.parser.parse(r_dict['CS002'][0])
+        time_diff = abs(expected_time - returned_time)
+        self.assertTrue(time_diff <= datetime.timedelta(days=0.5))
+
+    def test_util_target_transit_considers_stations(self):
+        stations = ['CS005', 'RS305', 'DE609']
+        r = requests.get(BASE_URL + '/util/target_transit?angle1=0.5&angle2=0.5&stations=%s' % ','.join(stations), auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # assert station is included in response and timestamps differ
+        target_transit_last = None
+        for station in stations:
+            self.assertIn(station, r_dict.keys())
+            target_transit = dateutil.parser.parse(r_dict[station][0])
+            if target_transit_last:
+                self.assertNotEqual(target_transit, target_transit_last)
+            target_transit_last = target_transit
+
+    def test_util_target_transit_considers_timestamps(self):
+        timestamps = ['2020-01-01', '2020-02-22T16-00-00', '2020-3-11']
+        r = requests.get(BASE_URL + '/util/target_transit?angle1=0.5&angle2=0.5&timestamps=%s' % ','.join(timestamps), auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # assert all requested timestamps yield a different response
+        transit_last = None
+        for i in range(len(timestamps)):
+            transit = r_dict['CS002'][i]
+            if transit_last:
+                self.assertNotEqual(transit, transit_last)
+            transit_last = transit
+
+    def test_util_target_transit_returns_correct_date_of_target_transit(self):
+        timestamps = ['2020-01-01T02-00-00']
+        r = requests.get(BASE_URL + '/util/target_transit?angle1=0.5&angle2=0.5&timestamps=%s' % ','.join(timestamps), auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+
+        # assert transit time is no further than 12h from requested time
+        requested_time = dateutil.parser.parse(timestamps[0]).replace(tzinfo=None)
+        returned_time = dateutil.parser.parse(r_dict['CS002'][0])
+        time_diff = abs(requested_time - returned_time)
+        self.assertTrue(time_diff <= datetime.timedelta(days=0.5))
+
+    def test_util_target_transit_considers_coordinates(self):
+        test_coords = [(0.5, 0.5, "J2000"), (0.6, 0.5, "J2000"), (0.6, 0.6, "J2000")]
+        transit_last = None
+        for coords in test_coords:
+            r = requests.get(BASE_URL + '/util/target_transit?angle1=%s&angle2=%s&direction_type=%s' % coords, auth=AUTH)
+            self.assertEqual(r.status_code, 200)
+            r_dict = json.loads(r.content.decode('utf-8'))
+
+            # assert all requested coordinates yield a response and times differ
+            transit = r_dict['CS002'][0]
+            if transit_last:
+                self.assertNotEqual(transit, transit_last)
+            transit_last = transit
 
 if __name__ == "__main__":
     os.environ['TZ'] = 'UTC'
diff --git a/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py b/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py
index b8d82ead9e47fbab49e00befc8742bedc634eee3..f1218237f3ff7b8ee8a7d70e24c3486d081cf500 100755
--- a/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py
+++ b/SAS/TMSS/backend/test/t_tmssapp_specification_django_API.py
@@ -45,6 +45,7 @@ from django.db.utils import IntegrityError
 from django.core.exceptions import ValidationError
 from django.db.models.deletion import ProtectedError
 from lofar.sas.tmss.tmss.exceptions import SchemaValidationException
+from lofar.sas.tmss.tmss.exceptions import TMSSException
 
 class GeneratorTemplateTest(unittest.TestCase):
     def test_GeneratorTemplate_gets_created_with_correct_creation_timestamp(self):
@@ -471,7 +472,7 @@ class SchedulingUnitDraftTest(unittest.TestCase):
             models.SchedulingUnitDraft.objects.create(**test_data)
 
     def test_SchedulingUnitDraft_gets_created_with_correct_default_ingest_permission_required(self):
-        
+
         # setup
         entry = models.SchedulingUnitDraft.objects.create(**SchedulingUnitDraft_test_data())
         #check the auto_ingest on project
@@ -749,239 +750,339 @@ class SchedulingUnitBlueprintTest(unittest.TestCase):
         self.assertEqual(scheduling_unit_blueprint.piggyback_allowed_tbb, scheduling_unit_draft.piggyback_allowed_tbb)
         self.assertEqual(scheduling_unit_blueprint.piggyback_allowed_aartfaac, scheduling_unit_draft.piggyback_allowed_aartfaac)
 
-
-class TaskBlueprintTest(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls) -> None:
-        cls.task_draft = models.TaskDraft.objects.create(**TaskDraft_test_data())
-        cls.scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
-
-    def test_TaskBlueprint_gets_created_with_correct_creation_timestamp(self):
-
-        # setup
-        before = datetime.utcnow()
-        entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-
-        after = datetime.utcnow()
-
-        # assert
-        self.assertLess(before, entry.created_at)
-        self.assertGreater(after, entry.created_at)
-
-    def test_TaskBlueprint_update_timestamp_gets_changed_correctly(self):
+    def test_SchedulingUnitBlueprint_gets_created_with_correct_default_scheduling_constraints(self):
 
         # setup
-        entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-        before = datetime.utcnow()
-        entry.save()
-        after = datetime.utcnow()
-
-        # assert
-        self.assertLess(before, entry.updated_at)
-        self.assertGreater(after, entry.updated_at)
-
-    def test_TaskBlueprint_prevents_missing_template(self):
-
-        # setup
-        test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-        test_data['specifications_template'] = None
-
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskBlueprint.objects.create(**test_data)
-
-    def test_TaskBlueprint_prevents_missing_draft(self):
+        constraints = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints'))
+        scheduling_unit_draft = models.SchedulingUnitDraft.objects.create(**SchedulingUnitDraft_test_data(
+            scheduling_constraints_doc={'foo': 'baz'},
+            scheduling_constraints_template=constraints))
+        scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data(draft=scheduling_unit_draft))
 
-        # setup
-        test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-        test_data['draft'] = None
+        scheduling_unit_blueprint.refresh_from_db()
 
         # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskBlueprint.objects.create(**test_data)
-
-    def test_TaskBlueprint_prevents_draft_deletion(self):
-        # setup
-        test_data = dict(TaskBlueprint_test_data())
-        blueprint = models.TaskBlueprint.objects.create(**test_data)
-        draft = blueprint.draft
-        with self.assertRaises(ProtectedError):
-            draft.delete()
+        self.assertEqual(scheduling_unit_blueprint.scheduling_constraints_doc, scheduling_unit_draft.scheduling_constraints_doc)
+        self.assertEqual(scheduling_unit_blueprint.scheduling_constraints_template, scheduling_unit_draft.scheduling_constraints_template)
 
-    def test_TaskBlueprint_prevents_missing_scheduling_unit_blueprint(self):
+    def test_SchedulingUnitBlueprint_prevents_updating_scheduling_constraints_template_if_not_in_correct_state(self):
 
         # setup
-        test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-        test_data['scheduling_unit_blueprint'] = None
-
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskBlueprint.objects.create(**test_data)
-
-    def test_TaskBlueprint_predecessors_and_successors_none(self):
-        task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-        task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-
-        self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
-        self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
-        self.assertEqual(set(), set(task_blueprint_1.successors.all()))
-        self.assertEqual(set(), set(task_blueprint_2.successors.all()))
-
-    def test_TaskBlueprint_predecessors_and_successors_simple(self):
-        task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
-        task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+        constraints_1 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_1'))
+        constraints_2 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_2'))
+        scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
+        scheduling_unit_blueprint.scheduling_constraints_template = constraints_1
+        scheduling_unit_blueprint.save()
+
+        task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint))
+        subtask = models.Subtask.objects.create(**Subtask_test_data())
+        subtask.task_blueprints.set([task_blueprint])
+        subtask.state = models.SubtaskState.objects.get(value='error')  # the derived SUB status is then also error
+        subtask.save()
 
-        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_2))
-
-        self.assertEqual(task_blueprint_1, task_blueprint_2.predecessors.all()[0])
-        self.assertEqual(task_blueprint_2, task_blueprint_1.successors.all()[0])
-
-    def test_TaskBlueprint_predecessors_and_successors_complex(self):
-        task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4())))
-        task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-        task_blueprint_3: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-        task_blueprint_4: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-        task_blueprint_5: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-        task_blueprint_6: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
-
-        # ST1 ---> ST3 ---> ST4
-        #      |        |
-        # ST2 -          -> ST5 ---> ST6
+        scheduling_unit_blueprint.refresh_from_db()
 
-        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_3))
-        trb1 = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_2, consumer=task_blueprint_3))
-        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_4))
-        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_5))
-        models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_5, consumer=task_blueprint_6))
+        # we should be able to modify other fields
+        scheduling_unit_blueprint.results_accepted = not scheduling_unit_blueprint.results_accepted
+        scheduling_unit_blueprint.save()
 
-        self.assertEqual(set((task_blueprint_1, task_blueprint_2)), set(task_blueprint_3.predecessors.all()))
-        self.assertEqual(set((task_blueprint_4, task_blueprint_5)), set(task_blueprint_3.successors.all()))
-        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_4.predecessors.all()))
-        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_5.predecessors.all()))
-        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_1.successors.all()))
-        self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_2.successors.all()))
-        self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
-        self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
-        self.assertEqual(set(), set(task_blueprint_4.successors.all()))
-        self.assertEqual(set((task_blueprint_6,)), set(task_blueprint_5.successors.all()))
+        # but scheduling constraints should be immutable
+        with self.assertRaises(TMSSException) as context:
+            scheduling_unit_blueprint.scheduling_constraints_template = constraints_2
+            scheduling_unit_blueprint.save()
 
+        self.assertIn('schedulable state', str(context.exception))
 
-class TaskRelationBlueprintTest(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls) -> None:
-        cls.producer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
-        cls.consumer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
+    def test_SchedulingUnitBlueprint_allows_updating_scheduling_constraints_template_if_in_correct_state(self):
 
-    def test_TaskRelationBlueprint_gets_created_with_correct_creation_timestamp(self):
         # setup
-        before = datetime.utcnow()
-        entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+        constraints_3 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_3'))
+        constraints_4 = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data(name='constraints_4'))
+        scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
+        scheduling_unit_blueprint.scheduling_constraints_template = constraints_3
+        scheduling_unit_blueprint.save()
 
-        after = datetime.utcnow()
+        task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint))
+        subtask = models.Subtask.objects.create(**Subtask_test_data())
+        subtask.task_blueprints.set([task_blueprint])
+        subtask.save()
 
-        # assert
-        self.assertLess(before, entry.created_at)
-        self.assertGreater(after, entry.created_at)
+        scheduling_unit_blueprint.refresh_from_db()
 
-    def test_TaskRelationBlueprint_update_timestamp_gets_changed_correctly(self):
-        # setup
-        entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-        before = datetime.utcnow()
-        entry.save()
-        after = datetime.utcnow()
+        # we can still change the constraints
+        scheduling_unit_blueprint.scheduling_constraints_template = constraints_4
+        scheduling_unit_blueprint.save()
 
-        # assert
-        self.assertLess(before, entry.updated_at)
-        self.assertGreater(after, entry.updated_at)
+    def test_SchedulingUnitBlueprint_prevents_updating_scheduling_constraints_doc_if_not_in_correct_state(self):
 
-    def test_TaskRelationBlueprint_prevents_missing_selection_template(self):
         # setup
-        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-        test_data['selection_template'] = None
+        scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
+        task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint))
+        subtask = models.Subtask.objects.create(**Subtask_test_data())
+        subtask.task_blueprints.set([task_blueprint])
+        subtask.state = models.SubtaskState.objects.get(value='error')  # the derived SUB status is then also error
+        subtask.save()
 
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskRelationBlueprint.objects.create(**test_data)
+        scheduling_unit_blueprint.refresh_from_db()
 
-    def test_TaskRelationBlueprint_prevents_missing_draft(self):
-        # setup
-        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-        test_data['draft'] = None
+        # we should be able to modify other fields
+        scheduling_unit_blueprint.results_accepted = not scheduling_unit_blueprint.results_accepted
+        scheduling_unit_blueprint.save()
 
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskRelationBlueprint.objects.create(**test_data)
+        # but scheduling constraints should be immutable
+        with self.assertRaises(TMSSException) as context:
+            scheduling_unit_blueprint.scheduling_constraints_doc = {'foo': 'matic'}
+            scheduling_unit_blueprint.save()
 
-    def test_TaskRelationBlueprint_prevents_missing_producer(self):
-        # setup
-        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-        test_data['producer'] = None
+        self.assertIn('schedulable state', str(context.exception))
 
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskRelationBlueprint.objects.create(**test_data)
+    def test_SchedulingUnitBlueprint_allows_updating_scheduling_constraints_doc_if_in_correct_state(self):
 
-    def test_TaskRelationBlueprint_prevents_missing_consumer(self):
         # setup
-        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-        test_data['consumer'] = None
-
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskRelationBlueprint.objects.create(**test_data)
+        scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
+        task_blueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(scheduling_unit_blueprint=scheduling_unit_blueprint))
+        subtask = models.Subtask.objects.create(**Subtask_test_data())
+        subtask.task_blueprints.set([task_blueprint])
+        subtask.save()
 
-    def test_TaskRelationBlueprint_prevents_missing_input(self):
-        # setup
-        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-        test_data['input_role'] = None
+        scheduling_unit_blueprint.refresh_from_db()
 
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskRelationBlueprint.objects.create(**test_data)
+        scheduling_unit_blueprint.scheduling_constraints_doc = {'foo': 'matic'}
+        scheduling_unit_blueprint.save()
 
-    def test_TaskRelationBlueprint_prevents_missing_output(self):
-        # setup
-        test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
-        test_data['output_role'] = None
 
-        # assert
-        with self.assertRaises(IntegrityError):
-            models.TaskRelationBlueprint.objects.create(**test_data)
-
-
-
-
-class TestStationTimeLine(unittest.TestCase):
-    """
-    Actually this simple testcase should be in a separate module (t_tmssapp_calculations_django_API.py)
-    but I was just lazy and spare some overhead and I just 'piggyback' with this module
-    """
-
-    def test_StationTimeline_raises_Error_on_duplicate_station_timeline(self):
-        """
-        Test if adding a duplicate station-timestamp combination leads to an Error and so data is not inserted
-        """
-        import datetime
-
-        test_data = {"station_name": "CS001",
-                     "timestamp": datetime.date(2021, 4, 1),
-                     "sunrise_start": datetime.datetime(year=2021, month=4, day=1, hour=6, minute=1, second=0),
-                     "sunrise_end": datetime.datetime(year=2021, month=4, day=1, hour=7, minute=2, second=0),
-                     "sunset_start": datetime.datetime(year=2021, month=4, day=1, hour=20, minute=31, second=0),
-                     "sunset_end": datetime.datetime(year=2021, month=4, day=1, hour=21, minute=33, second=0) }
-
-        models.StationTimeline.objects.create(**test_data)
-        with self.assertRaises(IntegrityError) as context:
-            models.StationTimeline.objects.create(**test_data)
-            self.assertIn('unique_station_time_line', str(context.exception))
-
-        self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 1)
-        self.assertEqual(len(models.StationTimeline.objects.all()), 1)
-        # Add a non-duplicate
-        test_data["station_name"] = "CS002"
-        models.StationTimeline.objects.create(**test_data)
-        self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 2)
-        self.assertEqual(len(models.StationTimeline.objects.all()), 2)
+# class TaskBlueprintTest(unittest.TestCase):
+#     @classmethod
+#     def setUpClass(cls) -> None:
+#         cls.task_draft = models.TaskDraft.objects.create(**TaskDraft_test_data())
+#         cls.scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.create(**SchedulingUnitBlueprint_test_data())
+#
+#     def test_TaskBlueprint_gets_created_with_correct_creation_timestamp(self):
+#
+#         # setup
+#         before = datetime.utcnow()
+#         entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#
+#         after = datetime.utcnow()
+#
+#         # assert
+#         self.assertLess(before, entry.created_at)
+#         self.assertGreater(after, entry.created_at)
+#
+#     def test_TaskBlueprint_update_timestamp_gets_changed_correctly(self):
+#
+#         # setup
+#         entry = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#         before = datetime.utcnow()
+#         entry.save()
+#         after = datetime.utcnow()
+#
+#         # assert
+#         self.assertLess(before, entry.updated_at)
+#         self.assertGreater(after, entry.updated_at)
+#
+#     def test_TaskBlueprint_prevents_missing_template(self):
+#
+#         # setup
+#         test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#         test_data['specifications_template'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskBlueprint.objects.create(**test_data)
+#
+#     def test_TaskBlueprint_prevents_missing_draft(self):
+#
+#         # setup
+#         test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#         test_data['draft'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskBlueprint.objects.create(**test_data)
+#
+#     def test_TaskBlueprint_prevents_draft_deletion(self):
+#         # setup
+#         test_data = dict(TaskBlueprint_test_data())
+#         blueprint = models.TaskBlueprint.objects.create(**test_data)
+#         draft = blueprint.draft
+#         with self.assertRaises(ProtectedError):
+#             draft.delete()
+#
+#     def test_TaskBlueprint_prevents_missing_scheduling_unit_blueprint(self):
+#
+#         # setup
+#         test_data = dict(TaskBlueprint_test_data(task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#         test_data['scheduling_unit_blueprint'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskBlueprint.objects.create(**test_data)
+#
+#     def test_TaskBlueprint_predecessors_and_successors_none(self):
+#         task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#         task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#
+#         self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
+#         self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
+#         self.assertEqual(set(), set(task_blueprint_1.successors.all()))
+#         self.assertEqual(set(), set(task_blueprint_2.successors.all()))
+#
+#     def test_TaskBlueprint_predecessors_and_successors_simple(self):
+#         task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#         task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=self.task_draft, scheduling_unit_blueprint=self.scheduling_unit_blueprint))
+#
+#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_2))
+#
+#         self.assertEqual(task_blueprint_1, task_blueprint_2.predecessors.all()[0])
+#         self.assertEqual(task_blueprint_2, task_blueprint_1.successors.all()[0])
+#
+#     def test_TaskBlueprint_predecessors_and_successors_complex(self):
+#         task_blueprint_1: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4())))
+#         task_blueprint_2: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+#         task_blueprint_3: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+#         task_blueprint_4: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+#         task_blueprint_5: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+#         task_blueprint_6: models.TaskBlueprint = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data(name=str(uuid.uuid4()), task_draft=task_blueprint_1.draft, scheduling_unit_blueprint=task_blueprint_1.scheduling_unit_blueprint))
+#
+#         # ST1 ---> ST3 ---> ST4
+#         #      |        |
+#         # ST2 -          -> ST5 ---> ST6
+#
+#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_1, consumer=task_blueprint_3))
+#         trb1 = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_2, consumer=task_blueprint_3))
+#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_4))
+#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_3, consumer=task_blueprint_5))
+#         models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=task_blueprint_5, consumer=task_blueprint_6))
+#
+#         self.assertEqual(set((task_blueprint_1, task_blueprint_2)), set(task_blueprint_3.predecessors.all()))
+#         self.assertEqual(set((task_blueprint_4, task_blueprint_5)), set(task_blueprint_3.successors.all()))
+#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_4.predecessors.all()))
+#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_5.predecessors.all()))
+#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_1.successors.all()))
+#         self.assertEqual(set((task_blueprint_3,)), set(task_blueprint_2.successors.all()))
+#         self.assertEqual(set(), set(task_blueprint_1.predecessors.all()))
+#         self.assertEqual(set(), set(task_blueprint_2.predecessors.all()))
+#         self.assertEqual(set(), set(task_blueprint_4.successors.all()))
+#         self.assertEqual(set((task_blueprint_6,)), set(task_blueprint_5.successors.all()))
+#
+#
+# class TaskRelationBlueprintTest(unittest.TestCase):
+#     @classmethod
+#     def setUpClass(cls) -> None:
+#         cls.producer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
+#         cls.consumer = models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())
+#
+#     def test_TaskRelationBlueprint_gets_created_with_correct_creation_timestamp(self):
+#         # setup
+#         before = datetime.utcnow()
+#         entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#
+#         after = datetime.utcnow()
+#
+#         # assert
+#         self.assertLess(before, entry.created_at)
+#         self.assertGreater(after, entry.created_at)
+#
+#     def test_TaskRelationBlueprint_update_timestamp_gets_changed_correctly(self):
+#         # setup
+#         entry = models.TaskRelationBlueprint.objects.create(**TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#         before = datetime.utcnow()
+#         entry.save()
+#         after = datetime.utcnow()
+#
+#         # assert
+#         self.assertLess(before, entry.updated_at)
+#         self.assertGreater(after, entry.updated_at)
+#
+#     def test_TaskRelationBlueprint_prevents_missing_selection_template(self):
+#         # setup
+#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#         test_data['selection_template'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskRelationBlueprint.objects.create(**test_data)
+#
+#     def test_TaskRelationBlueprint_prevents_missing_draft(self):
+#         # setup
+#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#         test_data['draft'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskRelationBlueprint.objects.create(**test_data)
+#
+#     def test_TaskRelationBlueprint_prevents_missing_producer(self):
+#         # setup
+#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#         test_data['producer'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskRelationBlueprint.objects.create(**test_data)
+#
+#     def test_TaskRelationBlueprint_prevents_missing_consumer(self):
+#         # setup
+#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#         test_data['consumer'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskRelationBlueprint.objects.create(**test_data)
+#
+#     def test_TaskRelationBlueprint_prevents_missing_input(self):
+#         # setup
+#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#         test_data['input_role'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskRelationBlueprint.objects.create(**test_data)
+#
+#     def test_TaskRelationBlueprint_prevents_missing_output(self):
+#         # setup
+#         test_data = dict(TaskRelationBlueprint_test_data(producer=self.producer, consumer=self.consumer))
+#         test_data['output_role'] = None
+#
+#         # assert
+#         with self.assertRaises(IntegrityError):
+#             models.TaskRelationBlueprint.objects.create(**test_data)
+#
+#
+#
+#
+# class TestStationTimeLine(unittest.TestCase):
+#     """
+#     Actually this simple testcase should be in a separate module (t_tmssapp_calculations_django_API.py)
+#     but I was just lazy and spare some overhead and I just 'piggyback' with this module
+#     """
+#
+#     def test_StationTimeline_raises_Error_on_duplicate_station_timeline(self):
+#         """
+#         Test if adding a duplicate station-timestamp combination leads to an Error and so data is not inserted
+#         """
+#         import datetime
+#
+#         test_data = {"station_name": "CS001",
+#                      "timestamp": datetime.date(2021, 4, 1),
+#                      "sunrise_start": datetime.datetime(year=2021, month=4, day=1, hour=6, minute=1, second=0),
+#                      "sunrise_end": datetime.datetime(year=2021, month=4, day=1, hour=7, minute=2, second=0),
+#                      "sunset_start": datetime.datetime(year=2021, month=4, day=1, hour=20, minute=31, second=0),
+#                      "sunset_end": datetime.datetime(year=2021, month=4, day=1, hour=21, minute=33, second=0) }
+#
+#         models.StationTimeline.objects.create(**test_data)
+#         with self.assertRaises(IntegrityError) as context:
+#             models.StationTimeline.objects.create(**test_data)
+#             self.assertIn('unique_station_time_line', str(context.exception))
+#
+#         self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 1)
+#         self.assertEqual(len(models.StationTimeline.objects.all()), 1)
+#         # Add a non-duplicate
+#         test_data["station_name"] = "CS002"
+#         models.StationTimeline.objects.create(**test_data)
+#         self.assertEqual(len(models.StationTimeline.objects.filter(timestamp=datetime.date(2021, 4, 1))), 2)
+#         self.assertEqual(len(models.StationTimeline.objects.all()), 2)
 
 
 if __name__ == "__main__":
diff --git a/SAS/TMSS/backend/test/tmss_test_data_django_models.py b/SAS/TMSS/backend/test/tmss_test_data_django_models.py
index 9b7024f59cb7d6f0f06e429dc72ffb08fd231ef2..538d5d7480920cc1ca4c924914f675e91e4c8892 100644
--- a/SAS/TMSS/backend/test/tmss_test_data_django_models.py
+++ b/SAS/TMSS/backend/test/tmss_test_data_django_models.py
@@ -191,7 +191,9 @@ def SchedulingSet_test_data(name="my_scheduling_set", project: models.Project=No
 
 def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_set: models.SchedulingSet=None,
                                   template: models.SchedulingUnitTemplate=None, requirements_doc: dict=None,
-                                  observation_strategy_template: models.SchedulingUnitObservingStrategyTemplate=None) -> dict:
+                                  observation_strategy_template: models.SchedulingUnitObservingStrategyTemplate=None,
+                                  scheduling_constraints_doc: dict=None,
+                                  scheduling_constraints_template: models.SchedulingConstraintsTemplate=None) -> dict:
     if scheduling_set is None:
         scheduling_set = models.SchedulingSet.objects.create(**SchedulingSet_test_data())
 
@@ -201,6 +203,12 @@ def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_se
     if requirements_doc is None:
         requirements_doc = get_default_json_object_for_schema(template.schema)
 
+    if scheduling_constraints_template is None:
+        scheduling_constraints_template = models.SchedulingConstraintsTemplate.objects.create(**SchedulingConstraintsTemplate_test_data())
+
+    if scheduling_constraints_doc is None:
+        scheduling_constraints_doc = get_default_json_object_for_schema(scheduling_constraints_template.schema)
+
     return {"name": name,
             "description": "",
             "tags": [],
@@ -210,7 +218,9 @@ def SchedulingUnitDraft_test_data(name="my_scheduling_unit_draft", scheduling_se
             "copies": None,
             "scheduling_set": scheduling_set,
             "requirements_template": template,
-            "observation_strategy_template": observation_strategy_template }
+            "observation_strategy_template": observation_strategy_template,
+            "scheduling_constraints_template": scheduling_constraints_template,
+            "scheduling_constraints_doc": scheduling_constraints_doc}
 
 def TaskDraft_test_data(name: str=None, specifications_template: models.TaskTemplate=None, specifications_doc: dict=None, scheduling_unit_draft: models.SchedulingUnitDraft=None, output_pinned=False) -> dict:
     if name is None:
@@ -251,6 +261,7 @@ def TaskRelationDraft_test_data(producer: models.TaskDraft = None, consumer: mod
             "selection_template": models.TaskRelationSelectionTemplate.objects.create(**TaskRelationSelectionTemplate_test_data())}
 
 def SchedulingUnitBlueprint_test_data(name=None, requirements_template: models.SchedulingUnitTemplate=None, draft=None, output_pinned=None) -> dict:
+
     if name is None:
         name = 'my_scheduling_unit_blueprint_' + str(uuid.uuid4())
 
diff --git a/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py b/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py
index 55d8da30199a0d79ac0aa9c43a6d67e465835931..2c3dd34f8f81bd2a256eaa7ffe5164408eb8de34 100644
--- a/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py
+++ b/SAS/TMSS/backend/test/tmss_test_environment_unittest_setup.py
@@ -86,11 +86,13 @@ def _call_API_and_assert_expected_response(test_instance, url, call, data, expec
         for key, value in expected_content.items():
             if key not in r_dict.keys():
                 logger.error('!!! Missing key: %s in %s', key, r_dict.keys())
-            test_instance.assertTrue(key in r_dict.keys())
+            test_instance.assertIn(key, r_dict.keys())
             if isinstance(value, models.Model):
                 value = str(value.pk)
                 value = value.replace(' ', '%20')
-                test_instance.assertTrue(str(value) in r_dict[key])
+                if str(value) not in r_dict[key]:
+                    logger.error('!!! Unexpected value of key=%s: expected=%s got=%s', key, value, r_dict[key])
+                test_instance.assertIn(str(value), r_dict[key])
             elif type(value) is list:
                 test_instance.assertEqual(sorted(value), sorted(r_dict[key]), msg="lists differ for key=%s"%key) # compare lists independent of ordering
             elif isinstance(value, datetime.datetime):
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js
index df3e6659276fc8b78c97288612fb751a5366d73d..a408ef43f60f5f51dff7bdcccff261d65a8d4647 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js
@@ -17,6 +17,7 @@ function Jeditor(props) {
     // console.log("In JEditor", props.schema);
     const editorRef = useRef(null);
     let pointingProps = useRef(null);
+    // let durationProps = useRef(null);
     let editor = null;
 
     /**
@@ -113,6 +114,7 @@ function Jeditor(props) {
             props.defintionFormatter(schema);
         }
         pointingProps = [];
+        // durationProps = [];
         // Customize the pointing property to capture angle1 and angle2 to specified format
         for (const definitionKey in schema.definitions) {
             if (definitionKey === 'pointing') {
@@ -133,6 +135,7 @@ function Jeditor(props) {
                 }
             }
         }
+        
     // Customize datatype of certain properties like subbands, duration, etc.,
         getCustomProperties(schema.properties);
         getCustomProperties(schema.definitions);
@@ -193,7 +196,8 @@ function Jeditor(props) {
             } 
             return errors;
         });
-        schema.format = "grid"
+        schema.format = "grid";
+        console.log(schema);
         const editorOptions = {
             form_name_root: "specification",
             schema: schema,
@@ -307,6 +311,7 @@ function Jeditor(props) {
                 newProperty.validationType = propertyKey === 'subbands'?'subband_list':'subband_list_optional';
                 properties[propertyKey] = newProperty;
             }   else if (propertyKey.toLowerCase() === 'duration') {
+            // }   else if (propertyValue['$id'] === '#duration') {
                 let newProperty = {
                     "type": "string",
                     "format": "time",
@@ -333,8 +338,8 @@ function Jeditor(props) {
                         }
                     }
                 };
-
                 properties[propertyKey] = newProperty;
+                // durationProps.push(propertyKey);
             }   else if (propertyValue instanceof Object) {
                 // by default previously, all field will have format as Grid, but this will fail for calendar, so added property called skipFormat
                 if (propertyKey !== 'properties' && propertyKey !== 'default' && !propertyValue.skipFormat) {
@@ -383,6 +388,7 @@ function Jeditor(props) {
                     updateInput(inputValue);
                 }
             }  else if (inputKey.toLowerCase() === 'duration') {
+            // }  else if (durationProps.indexOf(inputKey)) {
                 editorInput[inputKey] = getTimeInput(inputValue);
             }
         }
@@ -407,6 +413,7 @@ function Jeditor(props) {
                         (outputKey === 'list' && typeof(outputValue) === 'string')) {
                 editorOutput[outputKey] = getSubbandOutput(outputValue);
             } else if (outputKey.toLowerCase() === 'duration') {
+            // }  else if (durationProps.indexOf(outputKey)) {
                 const splitOutput = outputValue.split(':');
                 editorOutput[outputKey] = ((splitOutput[0] * 3600) + (splitOutput[1] * 60)  + parseInt(splitOutput[2]));
             }
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js
index 7f552ba2d3c3bf60ebff7705b5f98edc3f38c999..f10d5e44b551c9d7e326c3f11e67ce9c38252d7a 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js
@@ -18,6 +18,7 @@ import ProjectService from '../../services/project.service';
 import ScheduleService from '../../services/schedule.service';
 import TaskService from '../../services/task.service';
 import UIConstants from '../../utils/ui.constants';
+import ParserUtility from '../../utils/parser.utility';
 import PageHeader from '../../layout/components/PageHeader';
 import SchedulingConstraint from './Scheduling.Constraints';
 import Stations from './Stations';
@@ -62,6 +63,7 @@ export class SchedulingUnitCreate extends Component {
         this.schedulingSets = [];                   // All scheduling sets to be filtered for project
         this.observStrategies = [];                 // All Observing strategy templates
         this.taskTemplates = [];                    // All task templates to be filtered based on tasks in selected strategy template
+        this.taskTemplateSchemas = {};
         this.tooltipOptions = UIConstants.tooltipOptions;
         this.constraintTemplates = [];              
         this.nameInput = React.createRef();         // Ref to Name field for auto focus
@@ -135,74 +137,43 @@ export class SchedulingUnitCreate extends Component {
      * It generates the JSON schema for JSON editor and defult vales for the parameters to be captured
      * @param {number} strategyId 
      */
-    async changeStrategy (strategyId) {
+     async changeStrategy (strategyId) {
         const observStrategy = _.find(this.observStrategies, {'id': strategyId});
         let station_group = [];
-        const tasks = observStrategy.template.tasks;    
+        const tasks = observStrategy.template.tasks;
+        const parameters = observStrategy.template.parameters;
         let paramsOutput = {};
         let schema = { type: 'object', additionalProperties: false, 
                         properties: {}, definitions:{}
-                     };
-                     
-            // TODo: This schema reference resolving code has to be moved to common file and needs to rework
-            for (const taskName of _.keys(tasks)) {
+                        };
+        const $strategyRefs = await $RefParser.resolve(observStrategy.template);
+        // TODo: This schema reference resolving code has to be moved to common file and needs to rework
+        for (const param of parameters) {
+            let taskPaths = param.refs[0].split("/");
+            const taskName = taskPaths[2];
+            taskPaths = taskPaths.slice(4, taskPaths.length);
             const task = tasks[taskName];
-            //Resolve task from the strategy template
-            const $taskRefs = await $RefParser.resolve(task);
-
-            // Identify the task specification template of every task in the strategy template
             const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']});
-            schema['$id'] = taskTemplate.schema['$id'];
-            schema['$schema'] = taskTemplate.schema['$schema'];
             if (taskTemplate.type_value==='observation' && task.specifications_doc.station_groups) {
                 station_group = task.specifications_doc.station_groups;
             }
-            let index = 0;
-            for (const param of observStrategy.template.parameters) {
-                if (param.refs[0].indexOf(`/tasks/${taskName}`) > 0) {
-                    // Resolve the identified template
-                    const $templateRefs = await $RefParser.resolve(taskTemplate);
-                    let property = { };
-                    let tempProperty = null;
-                    const taskPaths = param.refs[0].split("/");
-                    // Get the property type from the template and create new property in the schema for the parameters
-                    try {
-                        const parameterRef = param.refs[0];//.replace(`#/tasks/${taskName}/specifications_doc`, '#/schema/properties');
-                        tempProperty = $templateRefs.get(parameterRef);
-                    //    property = _.cloneDeep(taskTemplate.schema.properties[taskPaths[4]]);
-                       
-                    }   catch(error) {
-                        tempProperty = _.cloneDeep(taskTemplate.schema.properties[taskPaths[4]]);
-                        if (tempProperty['$ref']) {
-                            tempProperty = await UtilService.resolveSchema(tempProperty);
-                            if (tempProperty.definitions && tempProperty.definitions[taskPaths[4]]) {
-                                schema.definitions = {...schema.definitions, ...tempProperty.definitions};
-                                tempProperty = tempProperty.definitions[taskPaths[4]];
-                            }   else if (tempProperty.properties && tempProperty.properties[taskPaths[4]]) {
-                                tempProperty = tempProperty.properties[taskPaths[4]];
-                            }
-                        }
-                        if (tempProperty.type === 'array' && taskPaths.length>6) {
-                            tempProperty = tempProperty.items.properties[taskPaths[6]];
-                        }
-                        property = tempProperty;
-                    }
-                    property.title = param.name;
-                    property.default = $taskRefs.get(param.refs[0].replace(`#/tasks/${taskName}`, '#'));
-                    paramsOutput[`param_${index}`] = property.default;
-                    schema.properties[`param_${index}`] = property;
-                    // Set property defintions taken from the task template in new schema
-                    for (const definitionName in taskTemplate.schema.definitions) {
-                        schema.definitions[definitionName] = taskTemplate.schema.definitions[definitionName];
-                        
-                    }
-                }
-                index++;
-               
+            let taskTemplateSchema = this.taskTemplateSchemas[task['specifications_template']];
+            if (!taskTemplateSchema) {
+                taskTemplateSchema = _.find(this.taskTemplates, {'name': task['specifications_template']}).schema;
+                taskTemplateSchema = await UtilService.resolveSchema(_.cloneDeep(taskTemplateSchema));
+                this.taskTemplateSchemas[task['specifications_template']] = taskTemplateSchema;
+            }
+            schema.definitions = {...schema.definitions, ...taskTemplateSchema.definitions};
+            taskPaths.reverse();
+            const paramProp = await ParserUtility.getParamProperty($strategyRefs, taskPaths, taskTemplateSchema, param);
+            schema.properties[param.name] = _.cloneDeep(paramProp);
+            if (schema.properties[param.name]) {
+                schema.properties[param.name].title = param.name;
+                schema.properties[param.name].default = $strategyRefs.get(param.refs[0]);
+                paramsOutput[param.name] = schema.properties[param.name].default; 
             }
-            
         }
-        this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput, stationGroup: station_group, isDirty: true});
+        this.setState({observStrategy: observStrategy, paramsSchema: _.cloneDeep(schema), paramsOutput: paramsOutput, stationGroup: station_group, isDirty: true});
         publish('edit-dirty', true);
 
         // Function called to clear the JSON Editor fields and reload with new schema
@@ -387,7 +358,7 @@ export class SchedulingUnitCreate extends Component {
         let observStrategy = _.cloneDeep(this.state.observStrategy);
         const $refs = await $RefParser.resolve(observStrategy.template);
         observStrategy.template.parameters.forEach(async(param, index) => {
-            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput[param.name]);
         });
         for (const taskName in observStrategy.template.tasks) {
             let task = observStrategy.template.tasks[taskName];
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js
index d28fc907e584d5c443998210e87a01b5a55321a1..a5741a57125fb6fb4fa82e7c729f5ceb80bb7c57 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js
@@ -21,6 +21,7 @@ import ProjectService from '../../services/project.service';
 import ScheduleService from '../../services/schedule.service';
 import TaskService from '../../services/task.service';
 import UIConstants from '../../utils/ui.constants';
+import ParserUtility from '../../utils/parser.utility';
 import SchedulingConstraint from './Scheduling.Constraints';
 import UtilService from '../../services/util.service';
 
@@ -54,6 +55,7 @@ export class EditSchedulingUnit extends Component {
         this.schedulingSets = [];                   // All scheduling sets to be filtered for project
         this.observStrategies = [];                 // All Observing strategy templates
         this.taskTemplates = [];                    // All task templates to be filtered based on tasks in selected strategy template             
+        this.taskTemplateSchemas = {};
         this.schedulingSets = [];                   
         this.observStrategies = [];
         this.taskTemplates = [];                    
@@ -84,73 +86,50 @@ export class EditSchedulingUnit extends Component {
      * It generates the JSON schema for JSON editor and defult vales for the parameters to be captured
      * @param {number} strategyId 
      */
-    async changeStrategy (strategyId) {
-        let tasksToUpdate = {};
+     async changeStrategy (strategyId) {
         const observStrategy = _.find(this.observStrategies, {'id': strategyId});
-        const tasks = observStrategy.template.tasks;    
+        let station_group = [];
+        let tasksToUpdate = {};
+        const tasks = observStrategy.template.tasks;
+        const parameters = observStrategy.template.parameters;
         let paramsOutput = {};
         let schema = { type: 'object', additionalProperties: false, 
                         properties: {}, definitions:{}
-                     };
+                        };
+        const $strategyRefs = await $RefParser.resolve(observStrategy.template);
         // TODo: This schema reference resolving code has to be moved to common file and needs to rework
-        for (const taskName in tasks)  {
+        for (const param of parameters) {
+            let taskPaths = param.refs[0].split("/");
+            const taskName = taskPaths[2];
+            tasksToUpdate[taskName] = taskName;
+            taskPaths = taskPaths.slice(4, taskPaths.length);
             const task = tasks[taskName];
             const taskDraft = this.state.taskDrafts.find(taskD => taskD.name === taskName);
             if (taskDraft) {
                 task.specifications_doc = taskDraft.specifications_doc;
             }
-            //Resolve task from the strategy template
-            const $taskRefs = await $RefParser.resolve(task);
-
-            // Identify the task specification template of every task in the strategy template
             const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']});
-            schema['$id'] = taskTemplate.schema['$id'];
-            schema['$schema'] = taskTemplate.schema['$schema'];
-            let index = 0;
-            for (const param of observStrategy.template.parameters) {
-                if (param.refs[0].indexOf(`/tasks/${taskName}`) > 0) {
-                    tasksToUpdate[taskName] = taskName;
-                    // Resolve the identified template
-                    const $templateRefs = await $RefParser.resolve(taskTemplate);
-                    let property = { };
-                    let tempProperty = null;
-                    const taskPaths = param.refs[0].split("/");
-                    // Get the property type from the template and create new property in the schema for the parameters
-                    try {
-                        const parameterRef = param.refs[0];//.replace(`#/tasks/${taskName}/specifications_doc`, '#/schema/properties');
-                        tempProperty = $templateRefs.get(parameterRef);
-                    }   catch(error) {
-                        tempProperty = _.cloneDeep(taskTemplate.schema.properties[taskPaths[4]]);
-                        if (tempProperty['$ref']) {
-                            tempProperty = await UtilService.resolveSchema(tempProperty);
-                            if (tempProperty.definitions && tempProperty.definitions[taskPaths[4]]) {
-                                schema.definitions = {...schema.definitions, ...tempProperty.definitions};
-                                tempProperty = tempProperty.definitions[taskPaths[4]];
-                            }   else if (tempProperty.properties && tempProperty.properties[taskPaths[4]]) {
-                                tempProperty = tempProperty.properties[taskPaths[4]];
-                            }
-                        }
-                        if (tempProperty.type === 'array' && taskPaths.length>6) {
-                            tempProperty = tempProperty.items.properties[taskPaths[6]];
-                        }
-                        property = tempProperty;
-                    }
-                    property.title = param.name;
-                    property.default = $taskRefs.get(param.refs[0].replace(`#/tasks/${taskName}`, '#'));
-                    paramsOutput[`param_${index}`] = property.default;
-                    schema.properties[`param_${index}`] = property;
-                    // Set property defintions taken from the task template in new schema
-                    for (const definitionName in taskTemplate.schema.definitions) {
-                        schema.definitions[definitionName] = taskTemplate.schema.definitions[definitionName];
-                    }
-                }
-                index++;
-            }
             if (taskTemplate.type_value==='observation' && task.specifications_doc.station_groups) {
-                tasksToUpdate[taskName] = taskName;
+                station_group = task.specifications_doc.station_groups;
+            }
+            let taskTemplateSchema = this.taskTemplateSchemas[task['specifications_template']];
+            if (!taskTemplateSchema) {
+                taskTemplateSchema = _.find(this.taskTemplates, {'name': task['specifications_template']}).schema;
+                taskTemplateSchema = await UtilService.resolveSchema(_.cloneDeep(taskTemplateSchema));
+                this.taskTemplateSchemas[task['specifications_template']] = taskTemplateSchema;
+            }
+            schema.definitions = {...schema.definitions, ...taskTemplateSchema.definitions};
+            taskPaths.reverse();
+            const paramProp = await ParserUtility.getParamProperty($strategyRefs, taskPaths, taskTemplateSchema, param);
+            schema.properties[param.name] = _.cloneDeep(paramProp);
+            if (schema.properties[param.name]) {
+                schema.properties[param.name].title = param.name;
+                schema.properties[param.name].default = $strategyRefs.get(param.refs[0]);
+                paramsOutput[param.name] = schema.properties[param.name].default; 
             }
         }
         this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput, tasksToUpdate: tasksToUpdate});
+        // this.setState({observStrategy: observStrategy, paramsSchema: _.cloneDeep(schema), paramsOutput: paramsOutput, stationGroup: station_group, isDirty: true});
 
         // Function called to clear the JSON Editor fields and reload with new schema
         if (this.state.editorFunction) {
@@ -354,7 +333,7 @@ export class EditSchedulingUnit extends Component {
             let observStrategy = _.cloneDeep(this.state.observStrategy);
             const $refs = await $RefParser.resolve(observStrategy.template);
             observStrategy.template.parameters.forEach(async(param, index) => {
-                $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+                $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput[param.name]);
             });
             const schUnit = { ...this.state.schedulingUnit };
             schUnit.scheduling_constraints_doc = constStrategy;
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js
index ff028172ae69ebe0768d5451823edda91486d744..eb74629686387616f53433214eacda870b96f19f 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js
@@ -34,6 +34,7 @@ import UtilService from '../../services/util.service';
 import Validator from  '../../utils/validator';
 import UnitConverter from '../../utils/unit.converter'
 import UIConstants from '../../utils/ui.constants';
+import ParserUtility from '../../utils/parser.utility';
 
 import moment from 'moment';
 import _ from 'lodash';
@@ -155,6 +156,7 @@ export class SchedulingSetCreate extends Component {
         this.schedulingSets = [];                   // All scheduling sets to be filtered for project
         this.observStrategies = [];                 // All Observing strategy templates
         this.taskTemplates = [];                    // All task templates to be filtered based on tasks in selected strategy template
+        this.taskTemplateSchemas = [];
         this.constraintTemplates = [];
         this.agSUWithDefaultValue = {'id': 0, 'suname': '', 'sudesc': ''};
         this.emptyAGSU = {};
@@ -483,6 +485,48 @@ export class SchedulingSetCreate extends Component {
     }
 
     async getTaskSchema(observStrategy) {
+        let station_group = [];
+        let tasksToUpdate = {};
+        if(observStrategy) {
+            const tasks = observStrategy.template.tasks;
+            const parameters = observStrategy.template.parameters;
+            let paramsOutput = {};
+            let schema = { type: 'object', additionalProperties: false, 
+                            properties: {}, definitions:{}
+                            };
+            const $strategyRefs = await $RefParser.resolve(observStrategy.template);
+            // TODo: This schema reference resolving code has to be moved to common file and needs to rework
+            for (const param of parameters) {
+                let taskPaths = param.refs[0].split("/");
+                const taskName = taskPaths[2];
+                tasksToUpdate[taskName] = taskName;
+                taskPaths = taskPaths.slice(4, taskPaths.length);
+                const task = tasks[taskName];
+                const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']});
+                if (taskTemplate.type_value==='observation' && task.specifications_doc.station_groups) {
+                    station_group = task.specifications_doc.station_groups;
+                }
+                let taskTemplateSchema = this.taskTemplateSchemas[task['specifications_template']];
+                if (!taskTemplateSchema) {
+                    taskTemplateSchema = _.find(this.taskTemplates, {'name': task['specifications_template']}).schema;
+                    taskTemplateSchema = await UtilService.resolveSchema(_.cloneDeep(taskTemplateSchema));
+                    this.taskTemplateSchemas[task['specifications_template']] = taskTemplateSchema;
+                }
+                schema.definitions = {...schema.definitions, ...taskTemplateSchema.definitions};
+                taskPaths.reverse();
+                const paramProp = await ParserUtility.getParamProperty($strategyRefs, taskPaths, taskTemplateSchema, param);
+                schema.properties[param.name] = _.cloneDeep(paramProp);
+                if (schema.properties[param.name]) {
+                    schema.properties[param.name].title = param.name;
+                    schema.properties[param.name].default = $strategyRefs.get(param.refs[0]);
+                    paramsOutput[param.name] = schema.properties[param.name].default; 
+                }
+            }
+            await this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput,defaultStationGroups: station_group, tasksToUpdate: tasksToUpdate});
+        }
+    }
+
+    /*async getTaskSchema(observStrategy) {
         let station_group = [];
         let tasksToUpdate = {};
         if(observStrategy) {
@@ -526,7 +570,11 @@ export class SchedulingSetCreate extends Component {
                                 tempProperty = await UtilService.resolveSchema(tempProperty);
                                 if (tempProperty.definitions && tempProperty.definitions[taskPaths[4]]) {
                                     schema.definitions = {...schema.definitions, ...tempProperty.definitions};
-                                    tempProperty = tempProperty.definitions[taskPaths[4]];
+                                    if (taskPaths.length>6) {
+                                        tempProperty = _.cloneDeep(tempProperty.definitions[taskPaths[4]]);
+                                    }   else {
+                                        tempProperty = {'$ref': `#/definitions/${taskPaths[4]}`};
+                                    }
                                 }   else if (tempProperty.properties && tempProperty.properties[taskPaths[4]]) {
                                     tempProperty = tempProperty.properties[taskPaths[4]];
                                 }
@@ -550,7 +598,7 @@ export class SchedulingSetCreate extends Component {
             }
             await this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput,defaultStationGroups: station_group, tasksToUpdate: tasksToUpdate});
         }
-    }
+    }*/
 
     /**
      * Resolve JSON Schema
@@ -1553,9 +1601,11 @@ export class SchedulingSetCreate extends Component {
     async prepareObservStrategyFromExcelValue(suRow) {
         let colKeys =  Object.keys(suRow);
         let paramsOutput = {};
+        let parameters = _.map(this.state.observStrategy.template.parameters, 'name');
         for(const colKey of colKeys)  {
             let prefix = colKey.split("~");  
-            if(colKey.startsWith('param_') && prefix.length > 1) {
+            // if(colKey.startsWith('param_') && prefix.length > 1) {
+            if(prefix.length > 1 && parameters.indexOf(prefix[0])>=0 ) {
                 var res =  Object.keys(suRow).filter(v => v.startsWith(prefix[0]));
                 if(res && res.length > 1) {
                     let res = paramsOutput[prefix[0]];
@@ -1591,7 +1641,8 @@ export class SchedulingSetCreate extends Component {
         let observStrategy = _.cloneDeep(this.state.observStrategy);
         const $refs = await $RefParser.resolve(observStrategy.template);
         observStrategy.template.parameters.forEach(async(param, index) => {
-            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+            // $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput['param_' + index]);
+            $refs.set(observStrategy.template.parameters[index]['refs'][0], this.state.paramsOutput[param.name]);
         });
         return observStrategy;
     }
@@ -1806,9 +1857,9 @@ export class SchedulingSetCreate extends Component {
                 }
                 
                 this.dialogContent = this.getSchedulingDialogContent;
-                this.onCancel = this.reset;
+                this.onCancel = null;
                 this.onClose = this.reset;
-                this.callBackFunction = this.reset;
+                this.callBackFunction = null;
                 this.setState({isDirty : false, showSpinner: false, confirmDialogVisible: true, /*dialog: dialog,*/ isAGLoading: true, copyHeader: false, rowData: []});
                 publish('edit-dirty', false);
             }  else  {
@@ -1921,18 +1972,20 @@ export class SchedulingSetCreate extends Component {
      * Refresh the grid with updated data
      */
       async reset() {
-        let schedulingUnitList = await ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId);
-        schedulingUnitList = _.filter(schedulingUnitList,{'observation_strategy_template_id': this.state.observStrategy.id}) ;
-        this.setState({
-            schedulingUnitList:  schedulingUnitList,
-            confirmDialogVisible: false,
-            isDirty: false
-        });
-        publish('edit-dirty', false);
-        this.isNewSet = false;
-        await this.prepareScheduleUnitListForGrid();
-        this.state.gridApi.setRowData(this.state.rowData);
-        this.state.gridApi.redrawRows();
+        if (this.state.confirmDialogVisible) {
+            let schedulingUnitList = await ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId);
+            schedulingUnitList = _.filter(schedulingUnitList,{'observation_strategy_template_id': this.state.observStrategy.id}) ;
+            this.setState({
+                schedulingUnitList:  schedulingUnitList,
+                confirmDialogVisible: false,
+                isDirty: false
+            });
+            publish('edit-dirty', false);
+            this.isNewSet = false;
+            await this.prepareScheduleUnitListForGrid();
+            this.state.gridApi.setRowData(this.state.rowData);
+            this.state.gridApi.redrawRows();
+        }
     }
     
      /**
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/parser.utility.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/parser.utility.js
new file mode 100644
index 0000000000000000000000000000000000000000..a60078d385a0b718828e1d7377f94f92caadeb4f
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/parser.utility.js
@@ -0,0 +1,53 @@
+import _ from 'lodash';
+import $RefParser from "@apidevtools/json-schema-ref-parser";
+
+const ParserUtility = {
+    /**
+     * Function to get the property of the parameter referred for the task.
+     * @param {*} $strategyRefs 
+     * @param {Array} paramPaths - Property reference path. 
+     * Example if the task parameter refers '#/tasks/Target Pointing 1/specifications_doc/SAPs/0/digital_pointing',
+     * then the parameter to be passed is [digital_pointing, 0, SAPs]
+     * @param {Object} taskTemplateSchema - JSON schema for the respective task template
+     * @returns 
+     */
+     getParamProperty: async($strategyRefs, paramPaths, taskTemplateSchema) => {
+        const $templateRefs = await $RefParser.resolve(taskTemplateSchema);
+        let pathIndex = 0;
+        let paramProp = {};
+        for (const paramPath of paramPaths) {
+            let property = taskTemplateSchema.properties[paramPath];
+            if (property) {
+                let rootPath = paramPaths.slice(0, pathIndex)
+                rootPath.reverse();
+                paramProp = _.cloneDeep(property);
+                if (rootPath.length > 0) {
+                    for (const path of rootPath) {
+                        if (paramProp[path]) {
+                            break;
+                        }   else {
+                            if (paramProp['$ref']) {
+                                paramProp = $templateRefs.get(paramProp['$ref']);
+                                if (paramProp.properties && paramProp.properties[path]) {
+                                    paramProp = paramProp.properties[path];
+                                }
+                            }   else {
+                                if (paramProp.type === "array") {
+                                    paramProp = paramProp.items.properties[path];
+                                }   else if (paramProp.type === "object") {
+                                    paramProp = paramProp.properties[path];
+                                }   else {
+                                    paramProp = paramProp[path];
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            pathIndex++;
+        }
+        return paramProp;
+    }
+}
+
+export default ParserUtility;
\ No newline at end of file