diff --git a/Docker/lofar-ci/Dockerfile_ci_sas b/Docker/lofar-ci/Dockerfile_ci_sas
index e49f816720424fb51a0a7c139c166f2622d881c4..be3799a44d6be5ad0e50ad01de0f9a348a57b942 100644
--- a/Docker/lofar-ci/Dockerfile_ci_sas
+++ b/Docker/lofar-ci/Dockerfile_ci_sas
@@ -7,7 +7,7 @@ ARG BASE_VERSION=latest
 FROM ci_base:$BASE_VERSION
 
 RUN echo "Installing packages for SAS..." && \
-    yum install -y log4cplus log4cplus-devel python3 python3-libs python3-devel boost readline-devel boost-devel binutils-devel boost-python36 boost-python36-devel gettext which openldap-devel npm nodejs git java-11-openjdk python-twisted-core
+    yum install -y log4cplus log4cplus-devel python3 python3-libs python3-devel boost readline-devel boost-devel binutils-devel boost-python36 boost-python36-devel gettext which openldap-devel git java-11-openjdk python-twisted-core
     
 # see https://www.postgresql.org/download/linux/redhat/ on how to install postgresql-server > 9.2 on centos7 
 RUN yum erase -y postgresql postgresql-server postgresql-devel && \
@@ -18,9 +18,12 @@ ENV PATH /usr/pgsql-9.6/bin:$PATH
 
 RUN pip3 install cython kombu lxml requests pygcn xmljson mysql-connector-python python-dateutil django djangorestframework djangorestframework-xml ldap==1.0.2 flask fabric coverage python-qpid-proton PyGreSQL numpy h5py psycopg2 testing.postgresql Flask-Testing scipy Markdown django-filter python-ldap python-ldap-test ldap3 djangorestframework django-jsonforms django-json-widget django-jsoneditor drf-yasg flex swagger-spec-validator django-auth-ldap mozilla-django-oidc jsonschema comet
 
-RUN npm install -g npx && \
-    npm install -g n && \
-    n stable && \
+# Note: nodejs now comes with npm, do not install the npm package separately, since that will be taken from the epel repo and is conflicting.
+RUN echo "Installing Nodejs packages..." && \
+    curl -sL https://rpm.nodesource.com/setup_14.x | bash - && \
+    yum install -y nodejs && \
+    npm -v && \
+    node -v && \
     npm install -g serve
 
 USER lofarsys
\ No newline at end of file
diff --git a/SAS/TMSS/src/remakemigrations.py b/SAS/TMSS/src/remakemigrations.py
index 03fdec4cec3f94aa2345d79cb6c91d279b51395c..503432045e7905079d295a0faba956a987eaa66c 100755
--- a/SAS/TMSS/src/remakemigrations.py
+++ b/SAS/TMSS/src/remakemigrations.py
@@ -76,9 +76,13 @@ class Migration(migrations.Migration):
     # Start SubTask id with 2 000 000 to avoid overlap with 'old' (test/production) OTDB
     operations = [ migrations.RunSQL('ALTER SEQUENCE tmssapp_SubTask_id_seq RESTART WITH 2000000;'),
                    migrations.RunPython(populate_choices),
+                   migrations.RunPython(populate_settings),
                    migrations.RunPython(populate_misc),
                    migrations.RunPython(populate_lofar_json_schemas),
-                   migrations.RunPython(populate_settings)]
+                   migrations.RunPython(populate_cycles),
+                   migrations.RunPython(populate_resources),
+                   migrations.RunPython(populate_projects),
+                   migrations.RunPython(populate_test_scheduling_set) ]
 """
 
 
diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
index 6da5be3850dc9afe0aa813a73a1b0843b7a4d612..8a7c77a6b19b7e4cca4f0c808e783a2cb0e83729 100644
--- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
+++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.12 on 2020-07-07 10:45
+# Generated by Django 3.0.6 on 2020-07-20 14:07
 
 from django.conf import settings
 import django.contrib.postgres.fields
@@ -307,6 +307,15 @@ class Migration(migrations.Migration):
                 ('create_function', models.CharField(help_text='Python function to call to execute the generator.', max_length=128)),
             ],
         ),
+        migrations.CreateModel(
+            name='PeriodCategory',
+            fields=[
+                ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
         migrations.CreateModel(
             name='Project',
             fields=[
@@ -327,27 +336,23 @@ class Migration(migrations.Migration):
             },
         ),
         migrations.CreateModel(
-            name='ProjectQuota',
+            name='ProjectCategory',
             fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('value', models.FloatField(help_text='Resource Quota value')),
+                ('value', models.CharField(max_length=128, primary_key=True, serialize=False, unique=True)),
             ],
+            options={
+                'abstract': False,
+            },
         ),
         migrations.CreateModel(
-            name='ResourceType',
+            name='ProjectQuota',
             fields=[
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
-                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128, primary_key=True, serialize=False)),
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('value', models.FloatField(help_text='Resource Quota value')),
             ],
-            options={
-                'abstract': False,
-            },
         ),
         migrations.CreateModel(
-            name='ResourceUnit',
+            name='ResourceType',
             fields=[
                 ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
                 ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
@@ -983,11 +988,6 @@ class Migration(migrations.Migration):
             name='project',
             field=models.ForeignKey(help_text='Project to which this scheduling set belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='scheduling_sets', to='tmssapp.Project'),
         ),
-        migrations.AddField(
-            model_name='resourcetype',
-            name='resource_unit',
-            field=models.ForeignKey(help_text='Unit of current resource.', on_delete=django.db.models.deletion.PROTECT, related_name='resource_types', to='tmssapp.ResourceUnit'),
-        ),
         migrations.AddField(
             model_name='projectquota',
             name='project',
@@ -1001,7 +1001,17 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name='project',
             name='cycles',
-            field=models.ManyToManyField(help_text='Cycles to which this project belongs (NULLable).', null=True, related_name='projects', to='tmssapp.Cycle'),
+            field=models.ManyToManyField(blank=True, help_text='Cycles to which this project belongs (NULLable).', related_name='projects', to='tmssapp.Cycle'),
+        ),
+        migrations.AddField(
+            model_name='project',
+            name='period_category',
+            field=models.ForeignKey(help_text='Period category.', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.PeriodCategory'),
+        ),
+        migrations.AddField(
+            model_name='project',
+            name='project_category',
+            field=models.ForeignKey(help_text='Project category.', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.ProjectCategory'),
         ),
         migrations.AddConstraint(
             model_name='generatortemplate',
diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py
index 5f71d8b3e7997447b18548b528557232bc454362..b24c0bccf0ded89c957fa2c6c53c62dea7428c71 100644
--- a/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py
+++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0002_populate.py
@@ -21,5 +21,6 @@ class Migration(migrations.Migration):
                    migrations.RunPython(populate_misc),
                    migrations.RunPython(populate_lofar_json_schemas),
                    migrations.RunPython(populate_cycles),
+                   migrations.RunPython(populate_resources),
                    migrations.RunPython(populate_projects),
                    migrations.RunPython(populate_test_scheduling_set) ]
diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py
index 4335bd06c161cecc4308e5d50c6be1ec57421ebb..a7e72818e4f329a04b263819737f5e61a3751bd3 100644
--- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py
+++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py
@@ -141,7 +141,8 @@ class SchedulingRelationPlacement(AbstractChoice):
         AFTER = "after"
         BEFORE = "before"
         PARALLEL = "parallel"
-       
+
+
 class Flag(AbstractChoice):
     """Defines the model and predefined list of possible Flags to be used in Setting.
     The items in the Choises class below are automagically populated into the database via a data migration."""
@@ -149,6 +150,28 @@ class Flag(AbstractChoice):
         AUTOSCHEDULE = "allow_scheduling_observations"
 
 
+class PeriodCategory(AbstractChoice):
+    """Defines the model and predefined list of possible period categories to be used in Project.
+        The items in the Choices class below are automagically populated into the database via a data migration."""
+
+    class Choices(Enum):
+        SINGLE_CYCLE = "single_cycle"
+        LONG_TERM = "long_term"
+        UNBOUNDED = "unbounded"
+
+
+class ProjectCategory(AbstractChoice):
+    """Defines the model and predefined list of possible project categories to be used in Project.
+        The items in the Choices class below are automagically populated into the database via a data migration."""
+
+    class Choices(Enum):
+        REGULAR = "regular"
+        USER_SHARED_SUPPORT = "user_shared_support"
+        COMMISSIONING = "commissioning"
+        DDT = "ddt"
+        TEST = "test"
+
+
 # concrete models
 
 class Setting(BasicCommon):
@@ -253,14 +276,39 @@ class Cycle(NamedCommonPK):
 
 
 class Project(NamedCommonPK):
-    # todo: cycles should be protected since we have to manually decide to clean up projects with a cycle or keep them without cycle, however, ManyToManyField does not allow for that
-    cycles = ManyToManyField('Cycle', related_name='projects', null=True, help_text='Cycles to which this project belongs (NULLable).')
+    cycles = ManyToManyField('Cycle', blank=True, related_name='projects', help_text='Cycles to which this project belongs (NULLable).')
     priority_rank = FloatField(null=False, help_text='Priority of this project w.r.t. other projects. Projects can interrupt observations of lower-priority projects.') # todo: add if needed: validators=[MinValueValidator(0.0), MaxValueValidator(1.0)]
     trigger_priority = IntegerField(default=1000, help_text='Priority of this project w.r.t. triggers.') # todo: verify meaning and add to help_text: "Triggers with higher priority than this threshold can interrupt observations of projects."
     can_trigger = BooleanField(default=False, help_text='True if this project is allowed to supply observation requests on the fly, possibly interrupting currently running observations (responsive telescope).')
     private_data = BooleanField(default=True, help_text='True if data of this project is sensitive. Sensitive data is not made public.')
     expert = BooleanField(default=False, help_text='Expert projects put more responsibility on the PI.')
     filler = BooleanField(default=False, help_text='Use this project to fill up idle telescope time.')
+    project_category = ForeignKey('ProjectCategory', null=True, on_delete=PROTECT, help_text='Project category.')
+    period_category = ForeignKey('PeriodCategory', null=True, on_delete=PROTECT, help_text='Period category.')
+
+    # JK, 29/07/20 - after discussion with Sander, it turns out that the ticket TMSS-277 was a misunderstanding.
+    #  'default' does not refer to 'default values' that are supposed to be filled in by the backend.
+    #  It was meant to be 'resource_types displayed in the frontend by default', where the other resource_types are
+    #  optionally added to the set of quota. These can then be customized in the frontend and are created by the
+    #  frontend in the backend, but no quota are intended to be added automatically. So nothing is really  needed in
+    #  the backend for this (apart from the set of predefined resource_types).
+    #  There was some open question on whether there may be a required subset of quota that have to be enforced. So
+    #  I'll leave this in for now, until that question is cleared up.
+    #
+    # # also create default project quotas when projects are created
+    # def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
+    #     creating = self._state.adding  # True on create, False on update
+    #     super().save(force_insert, force_update, using, update_fields)
+    #     if creating:
+    #         # todo: review these defaults for being sensible
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="lta_storage"), value=1024^4, project=self)
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="cep_storage"), value=1024^4, project=self)
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="cep_processing_time"), value=60*60*24, project=self)
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="lofar_observing_time"), value=60*60*24, project=self)
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="lofar_observing_time_prio_a"), value=60*60*12, project=self)
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="lofar_observing_time_prio_b"), value=60*60*12, project=self)
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="support_time"), value=60*60*6, project=self)
+    #         ProjectQuota.objects.create(resource_type=ResourceType.objects.get(name="number_of_triggers"), value=42, project=self)
 
 
 class ProjectQuota(Model):
@@ -270,12 +318,9 @@ class ProjectQuota(Model):
 
 
 class ResourceType(NamedCommonPK):
-    resource_unit = ForeignKey('ResourceUnit', related_name="resource_types", on_delete=PROTECT, help_text='Unit of current resource.')
-
-
-class ResourceUnit(NamedCommonPK):
     pass
 
+
 class SchedulingSet(NamedCommon):
     generator_doc = JSONField(null=True, help_text='Parameters for the generator (NULLable).')
     generator_template = ForeignKey('GeneratorTemplate', on_delete=SET_NULL, null=True, help_text='Generator for the scheduling units in this set (NULLable).')
diff --git a/SAS/TMSS/src/tmss/tmssapp/populate.py b/SAS/TMSS/src/tmss/tmssapp/populate.py
index 35ca8990516e6c2470f0f87791064dd2b2f435d8..c8ea9780f7d44872d2ab7d3859bfa6e8568cb4f6 100644
--- a/SAS/TMSS/src/tmss/tmssapp/populate.py
+++ b/SAS/TMSS/src/tmss/tmssapp/populate.py
@@ -36,7 +36,8 @@ def populate_choices(apps, schema_editor):
     :return: None
     '''
     for choice_class in [Role, Datatype, Dataformat, CopyReason,
-                        SubtaskState, SubtaskType, StationType, Algorithm, ScheduleMethod, SchedulingRelationPlacement, Flag]:
+                         SubtaskState, SubtaskType, StationType, Algorithm, ScheduleMethod, SchedulingRelationPlacement,
+                         Flag, ProjectCategory, PeriodCategory]:
         choice_class.objects.bulk_create([choice_class(value=x.value) for x in choice_class.Choices])
 
 def populate_settings(apps, schema_editor):
@@ -206,12 +207,14 @@ def populate_projects(apps, schema_editor):
 
 
 def populate_resources(apps, schema_editor):
-    ru_bytes = ResourceUnit.objects.create(name="bytes", description="Bytes")
-    ru_hours = ResourceUnit.objects.create(name="hours", description="duration in hours")
-
-    ResourceType.objects.create(name="lta_storage", description="Amount of storage in LTA", resource_unit=ru_bytes)
-    ResourceType.objects.create(name="cep_storage", description="Amount of storage at CEP processing cluster", resource_unit=ru_bytes)
-    ResourceType.objects.create(name="cep_processing_hours", description="Number of processing hours for CEP processing cluster", resource_unit=ru_hours)
+    ResourceType.objects.create(name="lta_storage", description="Amount of storage in the LTA (in bytes)")
+    ResourceType.objects.create(name="cep_storage", description="Amount of storage on the CEP processing cluster (in bytes)")
+    ResourceType.objects.create(name="cep_processing_time", description="Processing time on the CEP processing cluster (in seconds)")
+    ResourceType.objects.create(name="lofar_observing_time", description="Observing time (in seconds)")
+    ResourceType.objects.create(name="lofar_observing_time_prio_a", description="Observing time with priority A (in seconds)")
+    ResourceType.objects.create(name="lofar_observing_time_prio_b", description="Observing time with priority B (in seconds)")
+    ResourceType.objects.create(name="support_time", description="Support time by human (in seconds)")
+    ResourceType.objects.create(name="number_of_triggers", description="Number of trigger events (as integer)")
 
 
 def populate_misc(apps, schema_editor):
diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py
index 90152f462b3a860e144bb87a210f1c3a3c01bbdc..89dc4d9016dde72ba30fec93f94b7f408b4f643d 100644
--- a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py
+++ b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py
@@ -186,13 +186,6 @@ class ProjectQuotaSerializer(RelationalHyperlinkedModelSerializer):
         extra_fields = ['resource_type']
 
 
-class ResourceUnitSerializer(RelationalHyperlinkedModelSerializer):
-    class Meta:
-        model = models.ResourceUnit
-        fields = '__all__'
-        extra_fields = ['name']
-
-
 class ResourceTypeSerializer(RelationalHyperlinkedModelSerializer):
     class Meta:
         model = models.ResourceType
@@ -212,6 +205,18 @@ class SettingSerializer(serializers.ModelSerializer):
         fields = '__all__'
 
 
+class ProjectCategorySerializer(serializers.ModelSerializer):
+    class Meta:
+        model = models.ProjectCategory
+        fields = '__all__'
+
+
+class PeriodCategorySerializer(serializers.ModelSerializer):
+    class Meta:
+        model = models.PeriodCategory
+        fields = '__all__'
+
+
 class SchedulingSetSerializer(RelationalHyperlinkedModelSerializer):
 
     # Create a JSON editor form to replace the simple text field based on the schema in the template that this
diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py
index 93ab6971734d1c740e52c75b4c6e75407a5b7dd0..ffc8dcf51f465a2edef08732b3ccef9085e91d5a 100644
--- a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py
+++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py
@@ -147,11 +147,6 @@ class CopyReasonViewSet(LOFARViewSet):
     queryset = models.CopyReason.objects.all()
     serializer_class = serializers.CopyReasonSerializer
 
-
-class ResourceUnitViewSet(LOFARViewSet):
-    queryset = models.ResourceUnit.objects.all()
-    serializer_class = serializers.ResourceUnitSerializer
-
     
 class TaskConnectorTypeViewSet(LOFARViewSet):
     queryset = models.TaskConnectorType.objects.all()
@@ -186,6 +181,16 @@ class ProjectNestedViewSet(LOFARNestedViewSet):
 class ProjectQuotaViewSet(LOFARViewSet):
     queryset = models.ProjectQuota.objects.all()
     serializer_class = serializers.ProjectQuotaSerializer
+
+    def get_queryset(self):
+        queryset = models.ProjectQuota.objects.all()
+
+        # query by project
+        project = self.request.query_params.get('project', None)
+        if project is not None:
+            return queryset.filter(project=project)
+
+        return queryset
     
 
 class ResourceTypeViewSet(LOFARViewSet):
@@ -208,6 +213,16 @@ class SettingViewSet(LOFARViewSet):
     serializer_class = serializers.SettingSerializer
 
 
+class PeriodCategoryViewSet(LOFARViewSet):
+    queryset = models.PeriodCategory.objects.all()
+    serializer_class = serializers.PeriodCategorySerializer
+
+
+class ProjectCategoryViewSet(LOFARViewSet):
+    queryset = models.ProjectCategory.objects.all()
+    serializer_class = serializers.ProjectCategorySerializer
+
+
 class SchedulingUnitDraftViewSet(LOFARViewSet):
     queryset = models.SchedulingUnitDraft.objects.all()
     serializer_class = serializers.SchedulingUnitDraftSerializer
diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py
index 6dda70922b4b63118f7deed3c8e9c9a08b3dea9a..e49755be62a54b36af280073a128b72db1ae8ad3 100644
--- a/SAS/TMSS/src/tmss/urls.py
+++ b/SAS/TMSS/src/tmss/urls.py
@@ -87,6 +87,8 @@ router.register(r'datatype', viewsets.DatatypeViewSet)
 router.register(r'dataformat', viewsets.DataformatViewSet)
 router.register(r'copy_reason', viewsets.CopyReasonViewSet)
 router.register(r'flag', viewsets.FlagViewSet)
+router.register(r'period_category', viewsets.PeriodCategoryViewSet)
+router.register(r'project_category', viewsets.ProjectCategoryViewSet)
 
 # templates
 router.register(r'generator_template', viewsets.GeneratorTemplateViewSet)
@@ -102,7 +104,6 @@ router.register(r'default_task_relation_selection_template', viewsets.DefaultTas
 # instances
 router.register(r'cycle', viewsets.CycleViewSet)
 router.register(r'project', viewsets.ProjectViewSet)
-router.register(r'resource_unit', viewsets.ResourceUnitViewSet)
 router.register(r'resource_type', viewsets.ResourceTypeViewSet)
 router.register(r'project_quota', viewsets.ProjectQuotaViewSet)
 router.register(r'setting', viewsets.SettingViewSet)
diff --git a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py
index b55b2d4669438ae85fcca09021075d3d0c2e1dcf..d192f6486088fdf8d2860a7d90731150715b3b05 100755
--- a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py
+++ b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py
@@ -720,9 +720,11 @@ class ProjectTestCase(unittest.TestCase):
         project_test_data = test_data_creator.Project()
 
         # POST and GET a new item and assert correctness
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, project_test_data)
+        expected = project_test_data.copy()
+        expected.pop('project_quota')  # exclude project_quota from comparison, because these get auto-generated
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, expected)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, project_test_data)
+        GET_OK_and_assert_equal_expected_response(self, url, expected)
 
     def test_project_PUT_invalid_raises_error(self):
         PUT_and_assert_expected_response(self, BASE_URL + '/project/9876789876/', test_data_creator.Project(), 404, {})
@@ -731,40 +733,52 @@ class ProjectTestCase(unittest.TestCase):
         project_test_data = test_data_creator.Project()
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, project_test_data)
+        expected = project_test_data.copy()
+        expected.pop('project_quota')  # exclude project_quota from comparison, because these get auto-generated
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, expected)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, project_test_data)
+        GET_OK_and_assert_equal_expected_response(self, url, expected)
 
         # PUT new values, verify
         test_data = dict(test_data_creator.Project("other description"))
         test_data['name'] = project_test_data['name']  # since name is PK, need to keep that unchanged
-        PUT_and_assert_expected_response(self, url, test_data, 200, test_data)
-        GET_OK_and_assert_equal_expected_response(self, url, test_data)
+        expected = test_data.copy()
+        expected.pop('project_quota')  # exclude project_quota from comparison, because these get auto-generated
+        PUT_and_assert_expected_response(self, url, test_data, 200, expected)
+        GET_OK_and_assert_equal_expected_response(self, url, expected)
 
     def test_project_PATCH(self):
         project_test_data = test_data_creator.Project()
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, project_test_data)
+        expected = project_test_data.copy()
+        expected.pop('project_quota')  # exclude project_quota from comparison, because these get auto-generated
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, expected)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, project_test_data)
+        GET_OK_and_assert_equal_expected_response(self, url, expected)
 
         test_patch = {"priority_rank": 1.0,
                       "tags": ["SUPERIMPORTANT"]}
 
         # PATCH item and verify
         PATCH_and_assert_expected_response(self, url, test_patch, 200, test_patch)
-        expected_data = dict(project_test_data)
-        expected_data.update(test_patch)
-        GET_OK_and_assert_equal_expected_response(self, url, expected_data)
+        expected.update(test_patch)
+        GET_OK_and_assert_equal_expected_response(self, url, expected)
 
     def test_project_DELETE(self):
         project_test_data = test_data_creator.Project()
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, project_test_data)
+        expected = project_test_data.copy()
+        expected.pop('project_quota')  # exclude project_quota from comparison, because these get auto-generated
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, expected)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, project_test_data)
+        GET_OK_and_assert_equal_expected_response(self, url, expected)
+
+        # DELETE related auto-generated project_quota first
+        project_quotas = r_dict['project_quota']
+        for project_quota in project_quotas:
+            DELETE_and_assert_gone(self, project_quota)
 
         # DELETE and check it's gone
         DELETE_and_assert_gone(self, url)
@@ -776,13 +790,15 @@ class ProjectTestCase(unittest.TestCase):
         cycle_url = POST_and_assert_expected_response(self, BASE_URL + '/cycle/', cycle_test_data, 201, cycle_test_data)['url']
         test_data = dict(test_data_creator.Project())
         test_data['cycles'] = [cycle_url]
-        url = POST_and_assert_expected_response(self, BASE_URL + '/project/', test_data, 201, test_data)['url']
+        expected = test_data.copy()
+        expected.pop('project_quota')  # exclude project_quota from comparison, because these get auto-generated
+        url = POST_and_assert_expected_response(self, BASE_URL + '/project/', test_data, 201, expected)['url']
 
         # verify
-        GET_OK_and_assert_equal_expected_response(self, url, test_data)
+        GET_OK_and_assert_equal_expected_response(self, url, expected)
 
         # add project reference to cycle test data (we make Django add that to the cycle in serializer)
-        cycle_test_data['projects'] = [url]  # add the
+        cycle_test_data['projects'] = [url]
 
         # Try to DELETE dependency, verify that was not successful
         # Unfortunately we don't get a nice error in json, but a Django debug page on error 500...
@@ -907,7 +923,9 @@ class ProjectQuotaTestCase(unittest.TestCase):
 
         # POST new item with dependencies
         project_test_data = test_data_creator.Project()
-        project_url = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, project_test_data)['url']
+        expected = project_test_data.copy()
+        expected.pop('project_quota')  # exclude project_quota from comparison, because these get auto-generated
+        project_url = POST_and_assert_expected_response(self, BASE_URL + '/project/', project_test_data, 201, expected)['url']
 
         project_quota_test_data = dict(test_data_creator.ProjectQuota(project_url=project_url))
         project_quota_url = POST_and_assert_expected_response(self, BASE_URL + '/project_quota/', project_quota_test_data, 201, project_quota_test_data)['url']
@@ -1595,7 +1613,7 @@ class TaskRelationDraftTestCase(unittest.TestCase):
         # assert the returned list contains related items, A list of length 1 is retrieved
         GET_and_assert_in_expected_response_result_list(self, BASE_URL + '/task_draft/%s/task_relation_draft/' % task_draft_1.id, test_data_1, 1)
         # assert an existing related producer is returned
-   
+
 
 class SchedulingUnitBlueprintTestCase(unittest.TestCase):
     @classmethod
@@ -2335,7 +2353,7 @@ class TaskSchedulingRelationBlueprintTestCase(unittest.TestCase):
         # verify
         GET_OK_and_assert_equal_expected_response(self, url, tsrb_test_data)
 
-        #Get the URL of first task blueprint 
+        #Get the URL of first task blueprint
         test_data = dict(tsrb_test_data)
         task_blueprint_url=test_data['first']
 
diff --git a/SAS/TMSS/test/tmss_test_data_django_models.py b/SAS/TMSS/test/tmss_test_data_django_models.py
index 2d8a16334f71af6d49759d28f67f6540991b053c..e9db5ec6fd350385f65e60cef32c5f3e91c18670 100644
--- a/SAS/TMSS/test/tmss_test_data_django_models.py
+++ b/SAS/TMSS/test/tmss_test_data_django_models.py
@@ -111,18 +111,10 @@ def Project_test_data() -> dict:
                "expert": True,
                "filler": False}
 
-def ResourceUnit_test_data() -> dict:
-    return  {
-        "tags": [],
-        "description": 'my description ' + str(uuid.uuid4()),
-        "name": 'my_resource_unit_' + str(uuid.uuid4()),
-        }
-
 def ResourceType_test_data() -> dict:
     return  {
         "tags": [],
         "description": 'my description ' + str(uuid.uuid4()),
-        "resource_unit": models.ResourceUnit.objects.create(**ResourceUnit_test_data()),
         "name": 'my_resource_type_' + str(uuid.uuid4()),
      }
 
diff --git a/SAS/TMSS/test/tmss_test_data_rest.py b/SAS/TMSS/test/tmss_test_data_rest.py
index ada401713f68345bfd3bbf9374f62c6ecc7e2a84..a75637c6c484a5a4e589c3d5a2e81f25755e0dfa 100644
--- a/SAS/TMSS/test/tmss_test_data_rest.py
+++ b/SAS/TMSS/test/tmss_test_data_rest.py
@@ -141,22 +141,13 @@ class TMSSRESTTestDataCreator():
                 "priority_rank": 1.0,
                 "trigger_priority": 1000,
                 "can_trigger": False,
-                "private_data": True}
+                "private_data": True,
+                "cycles": []}
 
-    def ResourceUnit(self):
-            return  {
-                "tags": [],
-                "description": 'my description ' + str(uuid.uuid4()),
-                "name": 'my_resource_unit_' + str(uuid.uuid4())
-            }
-
-    def ResourceType(self, description="my resource_type description", resource_url=None):
-        if resource_url is None:
-            resource_url = self.post_data_and_get_url(self.ResourceUnit(), '/resource_unit/')
+    def ResourceType(self, description="my resource_type description"):
         return {
             "tags": [],
             "description": description,
-            "resource_unit": resource_url,
             "name": 'my_resource_type_' + str(uuid.uuid4())
         }