From f0f75ecaa85a70b8b6ed46e40c7bb3455b2a4712 Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 4 May 2025 21:14:04 +0930 Subject: [PATCH 001/175] chore(accounting): Add models directory ref: #740 #737 --- app/accounting/models/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/accounting/models/__init__.py diff --git a/app/accounting/models/__init__.py b/app/accounting/models/__init__.py new file mode 100644 index 00000000..e69de29b From 5d6d0e95ecf52e055561f46a20e25a6d9ca8c516 Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 4 May 2025 21:37:51 +0930 Subject: [PATCH 002/175] chore(accounting): Add viewsets directory ref: #741 #737 --- app/accounting/viewsets/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/accounting/viewsets/__init__.py diff --git a/app/accounting/viewsets/__init__.py b/app/accounting/viewsets/__init__.py new file mode 100644 index 00000000..e69de29b From 95aba2b44b79c4c3d48644c9141f8308085cf860 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 00:43:51 +0930 Subject: [PATCH 003/175] chore(accounting): Add permissions to permissions selector ref: #741 #737 --- app/access/functions/permissions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/access/functions/permissions.py b/app/access/functions/permissions.py index 56f69119..3b65f822 100644 --- a/app/access/functions/permissions.py +++ b/app/access/functions/permissions.py @@ -9,6 +9,7 @@ def permission_queryset(): apps = [ 'access', + 'accounting', 'assistance', 'config_management', 'core', From b11a9789622835c434855fe336e0d066d9722764 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 00:44:13 +0930 Subject: [PATCH 004/175] feat(accounting): Add Model AssetBase ref: #741 #737 --- app/accounting/models/asset_base.py | 358 ++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 app/accounting/models/asset_base.py diff --git a/app/accounting/models/asset_base.py b/app/accounting/models/asset_base.py new file mode 100644 index 00000000..7b0f626f --- /dev/null +++ b/app/accounting/models/asset_base.py @@ -0,0 +1,358 @@ +from django.apps import apps +from django.db import models + +from rest_framework.reverse import reverse + +from access.fields import AutoCreatedField, AutoLastModifiedField +from access.models.tenancy import TenancyObject + +from core.lib.feature_not_used import FeatureNotUsed + + + +class AssetBase( + TenancyObject, +): + """Asset Base Model + + This model forms the base of ALL asset models and contains the core + features for all sub-models. + + **Don't** use this model directly, it should be used via a sub-model. + """ + + app_namespace = 'accounting' + + + @property + def _base_model(self): + + return AssetBase + + + class Meta: + + ordering = [ + 'id' + ] + + sub_model_type = 'asset' + + verbose_name = "Asset" + + verbose_name_plural = "Assets" + + + def validate_not_null(field): + + if field is None: + + return False + + return True + + + + is_global = None + + + id = models.AutoField( + blank = False, + help_text = 'Ticket ID Number', + primary_key = True, + unique = True, + verbose_name = 'Number', + ) + + asset_number = models.CharField( + blank = True, + help_text = 'Number or tag to use to track this asset', + max_length = 30, + null = True, + unique = True, + verbose_name = 'Asset Number', + ) + + serial_number = models.CharField( + blank = True, + help_text = 'Serial number of this asset assigned by manufacturer.', + max_length = 30, + null = True, + unique = True, + verbose_name = 'Serial Number', + ) + + + # category = models.ForeignKey( + # TicketCategory, + # blank = False, + # help_text = 'Category for this asset', + # on_delete = models.PROTECT, + # verbose_name = 'Category', + # ) + """Category & Subcategory: Assets are grouped into broad categories like IT + equipment, vehicles, furniture, or heavy machinery, with further + subcategorization to increase granularity. This classification + facilitates reporting, lifecycle planning, and policy enforcement for + different asset types. + """ + + + + # Status + + # model (manufacturer / model) + + + + + @property + def get_model_type(self): + """Fetch the Ticket Type + + You can safely override this function as long as it's called or the + logic is included in your over-ridden function. + + Returns: + str: The models `Meta.verbose_name` in lowercase and without spaces + None: The ticket is for the Base class. Used to prevent creating a base ticket. + """ + + ticket_type = str(self._meta.sub_model_type).lower().replace(' ', '_') + + if ticket_type == 'ticket': + + return None + + return ticket_type + + + def get_model_type_choices(): + + choices = [] + + if apps.ready: + + all_models = apps.get_models() + + for model in all_models: + + if( + ( isinstance(model, AssetBase) or issubclass(model, AssetBase) ) + and AssetBase._meta.sub_model_type != 'asset' + + ): + + choices += [ (model._meta.sub_model_type, model._meta.verbose_name) ] + + + return choices + + asset_type = models.CharField( + blank = True, + choices = get_model_type_choices, + default = Meta.sub_model_type, + help_text = 'Asset Type. (derived from asset model)', + max_length = 30, + null = False, + validators = [ + validate_not_null + ], + verbose_name = 'Asset Type', + ) + + + + created = AutoCreatedField( + editable = True, + ) + + modified = AutoLastModifiedField() + + + page_layout: list = [ + { + "name": "Details", + "slug": "details", + "sections": [ + { + "layout": "double", + "left": [ + 'organization', + 'asset_type', + 'asset_number', + 'serial_number', + ], + "right": [ + 'model_notes', + 'created', + 'modified', + ] + } + ] + }, + { + "name": "Knowledge Base", + "slug": "kb_articles", + "sections": [ + { + "layout": "table", + "field": "knowledge_base", + } + ] + }, + { + "name": "Tickets", + "slug": "tickets", + "sections": [ + { + "layout": "table", + "field": "tickets", + } + ], + }, + { + "name": "Notes", + "slug": "notes", + "sections": [] + }, + ] + + table_fields: list = [ + 'id', + { + "field": "display_name", + "type": "link", + "key": "_self" + }, + 'asset_type', + 'asset_number', + 'serial_number', + 'organization', + 'created' + ] + + + def __str__(self): + + return self.asset_type + ' - ' + self.asset_number + + + + def get_related_field_name(self) -> str: + + meta = getattr(self, '_meta') + + for related_object in getattr(meta, 'related_objects', []): + + if not issubclass(related_object.related_model, self._base_model): + + continue + + if getattr(self, related_object.name, None): + + if( + not str(related_object.name).endswith('history') + and not str(related_object.name).endswith('notes') + ): + + return related_object.name + break + + + return '' + + + def get_related_model(self): + """Recursive model Fetch + + Returns the lowest model found in a chain of inherited models. + + Args: + model (models.Model, optional): Model to fetch the child model from. Defaults to None. + + Returns: + models.Model: Lowset model found in inherited model chain + """ + + related_model_name = self.get_related_field_name() + + related_model = getattr(self, related_model_name, None) + + if related_model_name == '': + + related_model = None + + elif related_model is None: + + related_model = self + + elif hasattr(related_model, 'get_related_field_name'): + + if related_model.get_related_field_name() != '': + + related_model = related_model.get_related_model() + + + return related_model + + + + + def get_url( self, request = None ) -> str: + + kwargs = self.get_url_kwargs() + + url_path_name = '_api_v2_asset_sub' + + if self._meta.sub_model_type == 'asset': + + url_path_name = '_api_v2_asset' + + if request: + + return reverse(f"v2:accounting:{url_path_name}-detail", request=request, kwargs = kwargs ) + + return reverse(f"v2:accounting:{url_path_name}-detail", kwargs = kwargs ) + + + + def get_url_kwargs(self) -> dict: + + kwargs = { + 'asset_model': self.asset_type, + } + + if self._meta.sub_model_type == 'asset': + + kwargs = {} + + + if self.id: + + kwargs.update({ + 'pk': self.id + }) + + return kwargs + + + def get_url_kwargs_notes(self): + + return FeatureNotUsed + + + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + + related_model = self.get_related_model() + + if related_model is None: + + related_model = self + + if self.asset_type != str(related_model._meta.sub_model_type).lower().replace(' ', '_'): + + self.asset_type = str(related_model._meta.sub_model_type).lower().replace(' ', '_') + + super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) From 471b5c08f66b60b89a9f99edaa10a690c35e996a Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 00:44:31 +0930 Subject: [PATCH 005/175] feat(accounting): Migrations for model AssetBase ref: #741 #737 --- app/accounting/migrations/0001_initial.py | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/accounting/migrations/0001_initial.py diff --git a/app/accounting/migrations/0001_initial.py b/app/accounting/migrations/0001_initial.py new file mode 100644 index 00000000..0e66e139 --- /dev/null +++ b/app/accounting/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 5.1.8 on 2025-05-04 12:42 + +import access.fields +import access.models.tenancy +import accounting.models.asset_base +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('access', '0005_entity_person_entityhistory_entitynotes_role_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='AssetBase', + fields=[ + ('model_notes', models.TextField(blank=True, default=None, help_text='Tid bits of information', null=True, verbose_name='Notes')), + ('id', models.AutoField(help_text='Ticket ID Number', primary_key=True, serialize=False, unique=True, verbose_name='Number')), + ('asset_number', models.CharField(blank=True, help_text='Number or tag to use to track this asset', max_length=30, null=True, unique=True, verbose_name='Asset Number')), + ('serial_number', models.CharField(blank=True, help_text='Serial number of this asset assigned by manufacturer.', max_length=30, null=True, unique=True, verbose_name='Serial Number')), + ('asset_type', models.CharField(blank=True, choices=accounting.models.asset_base.AssetBase.get_model_type_choices, default='asset', help_text='Asset Type. (derived from asset model)', max_length=30, validators=[accounting.models.asset_base.AssetBase.validate_not_null], verbose_name='Asset Type')), + ('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of creation', verbose_name='Created')), + ('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of last modification', verbose_name='Modified')), + ('organization', models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Organization')), + ], + options={ + 'verbose_name': 'Asset', + 'verbose_name_plural': 'Assets', + 'ordering': ['id'], + 'sub_model_type': 'asset', + }, + ), + ] From 018cd7d245a8fdf6fc4cd48c28c8616e6abea1f0 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 00:44:43 +0930 Subject: [PATCH 006/175] feat(accounting): Serializer for model AssetBase ref: #741 #737 --- app/accounting/serializers/asset.py | 108 ++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 app/accounting/serializers/asset.py diff --git a/app/accounting/serializers/asset.py b/app/accounting/serializers/asset.py new file mode 100644 index 00000000..96b6c9e9 --- /dev/null +++ b/app/accounting/serializers/asset.py @@ -0,0 +1,108 @@ +from rest_framework import serializers + +from drf_spectacular.utils import extend_schema_serializer + +from access.serializers.organization import OrganizationBaseSerializer + +from accounting.models.asset_base import AssetBase + +from api.serializers import common + + + +@extend_schema_serializer(component_name = 'AssetBaseBaseSerializer') +class BaseSerializer(serializers.ModelSerializer): + """Base Ticket Model""" + + + display_name = serializers.SerializerMethodField('get_display_name') + + def get_display_name(self, item) -> str: + + return str( item ) + + url = serializers.SerializerMethodField('get_url') + + def get_url(self, item) -> str: + + return item.get_url( request = self.context['view'].request ) + + + class Meta: + + model = AssetBase + + fields = [ + 'id', + 'display_name', + 'url', + ] + + read_only_fields = [ + 'id', + 'display_name', + 'url', + ] + + + +@extend_schema_serializer(component_name = 'AssetBaseModelSerializer') +class ModelSerializer( + common.CommonModelSerializer, + BaseSerializer +): + """Ticket Base Model""" + + + _urls = serializers.SerializerMethodField('get_url') + + + class Meta: + + model = AssetBase + + fields = [ + 'id', + 'display_name', + 'organization', + 'asset_type', + 'asset_number', + 'serial_number', + # 'status', + # 'category', + 'model_notes', + 'created', + 'modified', + '_urls', + ] + + read_only_fields = [ + 'id', + 'display_name', + 'asset_type', + 'created', + 'modified', + '_urls', + ] + + + def validate(self, attrs): + + attrs = super().validate( attrs ) + + return attrs + + + def is_valid(self, raise_exception = False): + + is_valid = super().is_valid( raise_exception = raise_exception ) + + return is_valid + + + +@extend_schema_serializer(component_name = 'AssetBaseViewSerializer') +class ViewSerializer(ModelSerializer): + """Ticket Base View Model""" + + organization = OrganizationBaseSerializer(many=False, read_only=True) From c8cec06d85b1e0eb1fa244aa27fe8a2de7171d73 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 00:48:10 +0930 Subject: [PATCH 007/175] feat(accounting): Viewset for Assets ref: #741 #737 --- app/accounting/urls.py | 43 +++++- app/accounting/viewsets/asset.py | 256 +++++++++++++++++++++++++++++++ app/api/react_ui_metadata.py | 10 ++ 3 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 app/accounting/viewsets/asset.py diff --git a/app/accounting/urls.py b/app/accounting/urls.py index 79b3e315..646b56c1 100644 --- a/app/accounting/urls.py +++ b/app/accounting/urls.py @@ -1,8 +1,49 @@ -from centurion_feature_flag.urls.routers import DefaultRouter +from django.apps import apps + +from centurion_feature_flag.urls.routers import APIRootView, DefaultRouter + +from accounting.viewsets import ( + asset +) + + + +class RootView(APIRootView): + + def get_view_name(self): + + return 'Accounting' + app_name = "accounting" router = DefaultRouter(trailing_slash=False) +router.APIRootView = RootView + + +asset_type_names = '' + + +for model in apps.get_models(): + + if issubclass(model, asset.AssetBase): + + if model._meta.sub_model_type == 'asset': + continue + + asset_type_names += model._meta.sub_model_type + '|' + + + +asset_type_names = str(asset_type_names)[:-1] + +if not asset_type_names: + asset_type_names = 'none' + + +router.register(f'asset/(?P[{asset_type_names}]+)?', asset.ViewSet, feature_flag = '2025-00004', basename='_api_v2_asset_sub') +router.register('asset', asset.NoDocsViewSet, feature_flag = '2025-00004', basename='_api_v2_asset') + urlpatterns = router.urls diff --git a/app/accounting/viewsets/asset.py b/app/accounting/viewsets/asset.py new file mode 100644 index 00000000..516f7b69 --- /dev/null +++ b/app/accounting/viewsets/asset.py @@ -0,0 +1,256 @@ +import importlib + +from django.apps import apps + +from drf_spectacular.utils import ( + extend_schema, + extend_schema_view, + OpenApiParameter, + OpenApiResponse, + PolymorphicProxySerializer +) + +from accounting.models.asset_base import AssetBase + +from api.viewsets.common import SubModelViewSet + + + +def spectacular_request_serializers( serializer_type = 'Model'): + + serializers: dict = {} + + + for model in apps.get_models(): + + if issubclass(model, AssetBase): + + serializer_name = 'asset' + + if( + model._meta.model_name == 'assetbase' + ): # Dont include base models + + continue + + + serializer_name += '_' + model._meta.sub_model_type + + serializer_module = importlib.import_module( + model._meta.app_label + '.serializers.' + str( + serializer_name + ) + ) + + serializers.update({ + str(model._meta.verbose_name).lower().replace(' ', '_'): getattr(serializer_module, serializer_type + 'Serializer') + }) + + return serializers + + + +@extend_schema_view( + create=extend_schema( + summary = 'Create an Asset', + description='.', + parameters = [ + OpenApiParameter( + name = 'asset_model', + description = 'Enter the asset type. This is the name of the asset sub-model.', + location = OpenApiParameter.PATH, + type = str, + required = False, + allow_blank = True, + ), + ], + request = PolymorphicProxySerializer( + component_name = 'Assets', + serializers = spectacular_request_serializers(), + resource_type_field_name = None, + many = False, + ), + responses = { + 200: OpenApiResponse( + description='Already exists', + response = PolymorphicProxySerializer( + component_name = 'Assets (View)', + serializers = spectacular_request_serializers( 'View' ), + resource_type_field_name = None, + many = False, + ) + ), + 201: OpenApiResponse( + description = 'Created', + response = PolymorphicProxySerializer( + component_name = 'Assets (View)', + serializers = spectacular_request_serializers( 'View' ), + resource_type_field_name = None, + many = False, + ) + ), + 403: OpenApiResponse(description='User is missing add permissions'), + } + ), + destroy = extend_schema( + summary = 'Delete an Asset', + description = '.', + parameters =[ + OpenApiParameter( + name = 'asset_model', + description = 'Enter the asset type. This is the name of the asset sub-model.', + location = OpenApiParameter.PATH, + type = str, + required = False, + allow_blank = True, + ), + ], + request = PolymorphicProxySerializer( + component_name = 'Assets', + serializers = spectacular_request_serializers(), + resource_type_field_name = None, + many = False, + ), + responses = { + 204: OpenApiResponse(description='Object deleted'), + 403: OpenApiResponse(description='User is missing delete permissions'), + } + ), + list = extend_schema( + summary = 'Fetch all Assets', + description='.', + parameters = [ + OpenApiParameter( + name = 'asset_model', + description = 'Enter the asset model. This is the name of the asset sub-model.', + location = OpenApiParameter.PATH, + type = str, + required = False, + allow_blank = True, + ), + ], + request = PolymorphicProxySerializer( + component_name = 'Assets', + serializers = spectacular_request_serializers(), + resource_type_field_name = None, + many = False, + ), + responses = { + 200: OpenApiResponse( + description='', + response = PolymorphicProxySerializer( + component_name = 'Assets (View)', + serializers = spectacular_request_serializers( 'View' ), + resource_type_field_name = None, + many = False, + ) + ), + 403: OpenApiResponse(description='User is missing view permissions'), + } + ), + retrieve = extend_schema( + summary = 'Fetch a single Asset', + description='.', + parameters = [ + OpenApiParameter( + name = 'asset_model', + description = 'Enter the asset model. This is the name of the Asset sub-model.', + location = OpenApiParameter.PATH, + type = str, + required = False, + allow_blank = True, + ), + ], + request = PolymorphicProxySerializer( + component_name = 'Assets', + serializers = spectacular_request_serializers(), + resource_type_field_name = None, + many = False, + ), + responses = { + 200: OpenApiResponse( + description='', + response = PolymorphicProxySerializer( + component_name = 'Assets (View)', + serializers = spectacular_request_serializers( 'View' ), + resource_type_field_name = None, + many = False, + ) + ), + 403: OpenApiResponse(description='User is missing view permissions'), + } + ), + update = extend_schema(exclude = True), + partial_update = extend_schema( + summary = 'Update an Asset', + description = '.', + parameters = [ + OpenApiParameter( + name = 'assets_model', + description = 'Enter the asset model. This is the name of the Asset sub-model.', + location = OpenApiParameter.PATH, + type = str, + required = False, + allow_blank = True, + ), + ], + request = PolymorphicProxySerializer( + component_name = 'Assets', + serializers = spectacular_request_serializers(), + resource_type_field_name = None, + many = False, + ), + responses = { + 200: OpenApiResponse( + description='', + response = PolymorphicProxySerializer( + component_name = 'Assets (View)', + serializers = spectacular_request_serializers( 'View' ), + resource_type_field_name = None, + many = False, + ) + ), + 403: OpenApiResponse(description='User is missing change permissions'), + } + ), +) +class ViewSet( SubModelViewSet ): + + _has_purge: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has purge permission. + """ + + base_model = AssetBase + + filterset_fields = [ + 'asset_type', + 'asset_number', + 'serial_number', + 'organization', + # 'is_deleted' + ] + + model_kwarg = 'asset_model' + + search_fields = [ + 'asset_number', + 'serial_number', + ] + + view_description = 'All Assets' + + + + +@extend_schema_view( # prevent duplicate documentation of both /accounting/asset endpoints + create = extend_schema(exclude = True), + destroy = extend_schema(exclude = True), + list = extend_schema(exclude = True), + retrieve = extend_schema(exclude = True), + update = extend_schema(exclude = True), + partial_update = extend_schema(exclude = True), +) +class NoDocsViewSet( ViewSet ): + pass diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index ddbe25e5..70444a2b 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -608,6 +608,16 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): } }) + if request.feature_flag['2025-00004']: + + nav['accounting']['pages'].update({ + 'view_assetbase': { + "display_name": "Assets", + "name": "asset", + "link": "/accounting/asset" + } + }) + if request.feature_flag['2025-00006']: nav['assistance']['pages'].update({ From 0ceab03334f4570a56240a90de968db949f37307 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 00:59:23 +0930 Subject: [PATCH 008/175] feat(itam): Add Model ITAssetBase ref: #741 #692 --- app/app/settings.py | 2 +- app/itam/models/itam_asset_base.py | 123 +++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 app/itam/models/itam_asset_base.py diff --git a/app/app/settings.py b/app/app/settings.py index 792bcf00..d3b8800f 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -18,7 +18,7 @@ from pathlib import Path from split_settings.tools import optional, include import django.db.models.options as options -options.DEFAULT_NAMES = (*options.DEFAULT_NAMES, 'sub_model_type') +options.DEFAULT_NAMES = (*options.DEFAULT_NAMES, 'sub_model_type', 'itam_sub_model_type') diff --git a/app/itam/models/itam_asset_base.py b/app/itam/models/itam_asset_base.py new file mode 100644 index 00000000..3a17fa99 --- /dev/null +++ b/app/itam/models/itam_asset_base.py @@ -0,0 +1,123 @@ +from django.apps import apps +from django.db import models + +from accounting.models.asset_base import AssetBase + + + +class ITAMAssetBase( + AssetBase, +): + """IT Asset Base Model + + This model forms the base of ALL IT asset models and contains the core + features for all sub-models. + + **Don't** use this model directly, it should be used via a sub-model. + """ + + + app_namespace = '' + + class Meta: + + ordering = [ + 'id' + ] + + sub_model_type = 'it_asset' + + itam_sub_model_type = 'itam_base' + + verbose_name = "IT Asset" + + verbose_name_plural = "IT Assets" + + + + @property + def get_itam_model_type(self): + """Fetch the ITAM Asset Type + + Returns: + str: The models `Meta.m` in lowercase and without spaces + None: The ticket is for the Base class. Used to prevent creating a base ticket. + """ + + model_type = str(self._meta.itam_sub_model_type).lower().replace(' ', '_') + + if model_type == 'itam_base': + + return None + + return model_type + + + def get_itam_model_type_choices(): + + choices = [] + + if apps.ready: + + all_models = apps.get_models() + + for model in all_models: + + if( + ( isinstance(model, ITAMAssetBase) or issubclass(model, ITAMAssetBase) ) + and ITAMAssetBase._meta.itam_sub_model_type != 'itam_base' + + ): + + choices += [ (model._meta.itam_sub_model_type, model._meta.verbose_name) ] + + + return choices + + itam_type = models.CharField( + blank = True, + choices = get_itam_model_type_choices, + default = Meta.itam_sub_model_type, + help_text = 'IT Asset Type. (derived from IT asset model)', + max_length = 30, + null = False, + verbose_name = 'IT Asset Type', + ) + + + # this model uses a custom page layout + page_layout: list = [] + + table_fields: list = [ + 'id', + { + "field": "display_name", + "type": "link", + "key": "_self" + }, + 'itam_type', + 'asset_number', + 'serial_number', + 'organization', + 'created' + ] + + + def __str__(self): + + return self._meta.verbose_name + ' - ' + self.asset_number + + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + + related_model = self.get_related_model() + + if related_model is None: + + related_model = self + + if self.itam_type != str(related_model._meta.itam_sub_model_type).lower().replace(' ', '_'): + + self.itam_type = str(related_model._meta.itam_sub_model_type).lower().replace(' ', '_') + + super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) From 9a88b756546aae0f2855778c01250dd8678ee406 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 00:59:51 +0930 Subject: [PATCH 009/175] feat(itam): Migrations for ITAssetBase ref: #741 #692 --- app/itam/migrations/0011_itamassetbase.py | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/itam/migrations/0011_itamassetbase.py diff --git a/app/itam/migrations/0011_itamassetbase.py b/app/itam/migrations/0011_itamassetbase.py new file mode 100644 index 00000000..7373fcc2 --- /dev/null +++ b/app/itam/migrations/0011_itamassetbase.py @@ -0,0 +1,31 @@ +# Generated by Django 5.1.8 on 2025-05-04 13:58 + +import django.db.models.deletion +import itam.models.itam_asset_base +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounting', '0001_initial'), + ('itam', '0010_alter_software_organization'), + ] + + operations = [ + migrations.CreateModel( + name='ITAMAssetBase', + fields=[ + ('assetbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='accounting.assetbase')), + ('itam_type', models.CharField(blank=True, choices=itam.models.itam_asset_base.ITAMAssetBase.get_itam_model_type_choices, default='itam_base', help_text='IT Asset Type. (derived from IT asset model)', max_length=30, verbose_name='IT Asset Type')), + ], + options={ + 'verbose_name': 'IT Asset', + 'verbose_name_plural': 'IT Assets', + 'ordering': ['id'], + 'sub_model_type': 'it_asset', + 'itam_sub_model_type': 'itam_base', + }, + bases=('accounting.assetbase',), + ), + ] From efde919689e4cff5f02b20ba40e07215eed2a720 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 01:00:14 +0930 Subject: [PATCH 010/175] feat(itam): Serializer for ITAssetBase ref: #741 #692 --- app/api/urls_v2.py | 4 ++ app/itam/serializers/asset_it_asset.py | 83 ++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 app/itam/serializers/asset_it_asset.py diff --git a/app/api/urls_v2.py b/app/api/urls_v2.py index 8c004528..739c22a6 100644 --- a/app/api/urls_v2.py +++ b/app/api/urls_v2.py @@ -225,6 +225,10 @@ router.register('core/(?P[a-z_]+)/(?P[0-9]+)/item_ticket', router.register('itam', itam_index_v2.Index, basename='_api_v2_itam_home') + +# from accounting.viewsets import asset +# router.register('itam/(?Pdevice)?', asset.ViewSet, feature_flag = '2025-00004', basename='_api_v2_device') + router.register('itam/device', device_v2.ViewSet, basename='_api_v2_device') router.register('itam/device/(?P[0-9]+)/operating_system', device_operating_system.ViewSet, basename='_api_v2_device_operating_system') router.register('itam/device/(?P[0-9]+)/software', device_software_v2.ViewSet, basename='_api_v2_device_software') diff --git a/app/itam/serializers/asset_it_asset.py b/app/itam/serializers/asset_it_asset.py new file mode 100644 index 00000000..82f201c7 --- /dev/null +++ b/app/itam/serializers/asset_it_asset.py @@ -0,0 +1,83 @@ +from rest_framework import serializers + +from drf_spectacular.utils import extend_schema_serializer + +from access.serializers.organization import OrganizationBaseSerializer + +from accounting.serializers.asset import ( + BaseSerializer, + ModelSerializer as AssetBaseModelSerializer, + ViewSerializer as AssetBaseViewSerializer, +) + +from itam.models.itam_asset_base import ITAMAssetBase + + + +@extend_schema_serializer(component_name = 'ITAssetBaseModelSerializer') +class ModelSerializer( + AssetBaseModelSerializer, + BaseSerializer +): + """IT Asset Base Model""" + + + _urls = serializers.SerializerMethodField('get_url') + + + + class Meta: + + model = ITAMAssetBase + + fields = [ + 'id', + 'display_name', + 'organization', + 'itam_type', + 'asset_number', + 'serial_number', + + # 'status', + + # 'category', + + 'created', + 'modified', + '_urls', + ] + + read_only_fields = [ + 'id', + 'display_name', + 'itam_type', + 'created', + 'modified', + '_urls', + ] + + + + def validate(self, attrs): + + attrs = super().validate( attrs ) + + return attrs + + + def is_valid(self, raise_exception = False): + + is_valid = super().is_valid( raise_exception = raise_exception ) + + return is_valid + + + +@extend_schema_serializer(component_name = 'ITAssetBaseViewSerializer') +class ViewSerializer( + AssetBaseViewSerializer, + ModelSerializer +): + """IT Asset Base View Model""" + + organization = OrganizationBaseSerializer(many=False, read_only=True) From 370b8cd40f8ce9ec9dfd3d9e83bf21101e818b03 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 02:43:09 +0930 Subject: [PATCH 011/175] test(accounting): Unit Model checks for AssetBase Model ref: #741 #737 --- .../tests/unit/asset_base/conftest.py | 14 ++ .../asset_base/test_unit_asset_base_model.py | 164 ++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 app/accounting/tests/unit/asset_base/conftest.py create mode 100644 app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py diff --git a/app/accounting/tests/unit/asset_base/conftest.py b/app/accounting/tests/unit/asset_base/conftest.py new file mode 100644 index 00000000..b5335256 --- /dev/null +++ b/app/accounting/tests/unit/asset_base/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from accounting.models.asset_base import AssetBase + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = AssetBase + + yield request.cls.model + + del request.cls.model diff --git a/app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py b/app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py new file mode 100644 index 00000000..1c5e3896 --- /dev/null +++ b/app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py @@ -0,0 +1,164 @@ +import pytest + +from django.db import models + +from accounting.models.asset_base import AssetBase + +from app.tests.unit.test_unit_models import ( + PyTestTenancyObjectInheritedCases, +) + + + +class AssetBaseModelTestCases( + PyTestTenancyObjectInheritedCases, +): + + base_model = AssetBase + + kwargs_create_item: dict = { + 'asset_number': 'a12s432', + 'serial_number': 'abbcccdddd', + } + + sub_model_type = 'asset' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + + parameterized_fields: dict = { + "is_global": { + 'field_type': None, + 'field_parameter_default_exists': None, + 'field_parameter_default_value': None, + 'field_parameter_verbose_name_type': None + }, + "asset_number": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "serial_number": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + } + + } + + + + @pytest.fixture( scope = 'class') + def setup_model(self, + request, + model, + django_db_blocker, + organization_one, + organization_two + ): + + with django_db_blocker.unblock(): + + request.cls.organization = organization_one + + request.cls.different_organization = organization_two + + kwargs_create_item = {} + + for base in reversed(request.cls.__mro__): + + if hasattr(base, 'kwargs_create_item'): + + if base.kwargs_create_item is None: + + continue + + kwargs_create_item.update(**base.kwargs_create_item) + + + if len(kwargs_create_item) > 0: + + request.cls.kwargs_create_item = kwargs_create_item + + + if 'organization' not in request.cls.kwargs_create_item: + + request.cls.kwargs_create_item.update({ + 'organization': request.cls.organization + }) + + yield + + with django_db_blocker.unblock(): + + del request.cls.kwargs_create_item + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_model, + create_model, + ): + + pass + + + + def test_class_inherits_assetbase(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory + """ + + assert issubclass(self.model, AssetBase) + + + + def test_attribute_type_app_namespace(self): + """Attribute Type + + app_namespace is of type str + """ + + assert type(self.model.app_namespace) is str + + + def test_attribute_value_app_namespace(self): + """Attribute Type + + app_namespace has been set, override this test case with the value + of attribute `app_namespace` + """ + + assert self.model.app_namespace == 'accounting' + + + +class AssetBaseModelInheritedCases( + AssetBaseModelTestCases, +): + """Sub-Ticket Test Cases + + Test Cases for Ticket models that inherit from model AssetBase + """ + + kwargs_create_item: dict = {} + + model = None + + + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ + + + +class AssetBaseModelPyTest( + AssetBaseModelTestCases, +): + + pass From ce64664447e30863bb2902202cc769d671e16fae Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 02:43:47 +0930 Subject: [PATCH 012/175] test(base): update Model base test suite for model_notes field ref: #741 #737 --- app/accounting/tests/unit/asset_base/__init__.py | 0 app/app/tests/unit/test_unit_models.py | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 app/accounting/tests/unit/asset_base/__init__.py diff --git a/app/accounting/tests/unit/asset_base/__init__.py b/app/accounting/tests/unit/asset_base/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/app/tests/unit/test_unit_models.py b/app/app/tests/unit/test_unit_models.py index 56487c16..2611e616 100644 --- a/app/app/tests/unit/test_unit_models.py +++ b/app/app/tests/unit/test_unit_models.py @@ -405,6 +405,7 @@ class ModelFieldsTestCasesReWrite: parameterized_fields: dict = { "organization": { 'field_type': fields.Field, + 'field_parameter_default_exists': False, 'field_parameter_verbose_name_type': str }, "model_notes": { @@ -413,6 +414,8 @@ class ModelFieldsTestCasesReWrite: }, "is_global": { 'field_type': fields.BooleanField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': False, 'field_parameter_verbose_name_type': str } } @@ -493,6 +496,10 @@ class ModelFieldsTestCasesReWrite: assert self.model._meta.get_field(param_field_name).default == fields.NOT_PROVIDED + elif param_field_parameter_default_exists is None: + + assert True + else: assert self.model._meta.get_field(param_field_name).has_default() == param_field_parameter_default_exists @@ -507,7 +514,13 @@ class ModelFieldsTestCasesReWrite: During field creation, paramater `verbose_name` must not be `None` or empty ('') """ - assert getattr(self.model._meta.get_field(param_field_name), 'default') == param_field_parameter_default_value + if param_field_parameter_default_value is None: + + assert True + + else: + + assert getattr(self.model._meta.get_field(param_field_name), 'default') == param_field_parameter_default_value From 349d67fa7b30e2f98b34337837f721f7e0ce9afa Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 02:46:19 +0930 Subject: [PATCH 013/175] test(itam): Unit Model checks for ITAMAssetBase Model ref: #741 #692 --- app/itam/models/itam_asset_base.py | 2 +- .../tests/unit/itam_asset_base/__init__.py | 0 .../tests/unit/itam_asset_base/conftest.py | 14 +++ .../test_unit_itam_asset_base_model.py | 88 +++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 app/itam/tests/unit/itam_asset_base/__init__.py create mode 100644 app/itam/tests/unit/itam_asset_base/conftest.py create mode 100644 app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py diff --git a/app/itam/models/itam_asset_base.py b/app/itam/models/itam_asset_base.py index 3a17fa99..b9a9086c 100644 --- a/app/itam/models/itam_asset_base.py +++ b/app/itam/models/itam_asset_base.py @@ -17,7 +17,7 @@ class ITAMAssetBase( """ - app_namespace = '' + app_namespace = None class Meta: diff --git a/app/itam/tests/unit/itam_asset_base/__init__.py b/app/itam/tests/unit/itam_asset_base/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/itam/tests/unit/itam_asset_base/conftest.py b/app/itam/tests/unit/itam_asset_base/conftest.py new file mode 100644 index 00000000..eeae4944 --- /dev/null +++ b/app/itam/tests/unit/itam_asset_base/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from itam.models.itam_asset_base import ITAMAssetBase + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = ITAMAssetBase + + yield request.cls.model + + del request.cls.model diff --git a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py new file mode 100644 index 00000000..52ac956a --- /dev/null +++ b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py @@ -0,0 +1,88 @@ +from django.db import models + +from accounting.tests.unit.asset_base.test_unit_asset_base_model import AssetBaseModelInheritedCases + +from itam.models.itam_asset_base import ITAMAssetBase + + + +class ITAMAssetBaseModelTestCases( + AssetBaseModelInheritedCases, +): + + kwargs_create_item: dict = {} + + sub_model_type = 'itam_base' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + + parameterized_fields: dict = { + "itam_type": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': ITAMAssetBase._meta.itam_sub_model_type, + 'field_parameter_verbose_name_type': str + } + } + + + + def test_class_inherits_itam_assetbase(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory + """ + + assert issubclass(self.model, ITAMAssetBase) + + + + def test_attribute_type_app_namespace(self): + """Attribute Type + + app_namespace is of type str + """ + + assert self.model.app_namespace is None + + + def test_attribute_value_app_namespace(self): + """Attribute Type + + app_namespace has been set, override this test case with the value + of attribute `app_namespace` + """ + + assert self.model.app_namespace is None + + + +class ITAMAssetBaseModelInheritedCases( + ITAMAssetBaseModelTestCases, +): + """Sub-Ticket Test Cases + + Test Cases for Ticket models that inherit from model AssetBase + """ + + kwargs_create_item: dict = {} + + model = None + + + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ + + + +class ITAMAssetBaseModelPyTest( + ITAMAssetBaseModelTestCases, +): + + pass From 9561301500c61c42405d6f385887b79cd7912cfe Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 02:53:54 +0930 Subject: [PATCH 014/175] test(accounting): Unit Viewset checks for AssetBase Model ref: #741 #737 --- .../test_unit_asset_base_viewset.py | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py diff --git a/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py b/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py new file mode 100644 index 00000000..b4095e6a --- /dev/null +++ b/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py @@ -0,0 +1,102 @@ +from django.test import Client, TestCase + +from rest_framework.reverse import reverse + + +from accounting.viewsets.asset import ( + NoDocsViewSet, + AssetBase, + ViewSet, +) + +from api.tests.unit.test_unit_common_viewset import SubModelViewSetInheritedCases + + + +class AssetBaseViewsetTestCases( + SubModelViewSetInheritedCases, +): + + model = AssetBase + + kwargs = None + + viewset = ViewSet + + base_model = AssetBase + + route_name = None + + + @classmethod + def setUpTestData(self): + """Setup Test + + 1. make list request + """ + + + super().setUpTestData() + + + client = Client() + + url = reverse( + self.route_name + '-list', + kwargs = self.kwargs + ) + + client.force_login(self.view_user) + + self.http_options_response_list = client.options(url) + + + + def test_view_attr_value_model_kwarg(self): + """Attribute Test + + Attribute `model_kwarg` must be equal to model._meta.sub_model_type + """ + + view_set = self.viewset() + + assert view_set.model_kwarg == 'asset_model' + + + +class AssetBaseViewsetInheritedCases( + AssetBaseViewsetTestCases, +): + """Test Suite for Sub-Models of TicketBase + + Use this Test suit if your sub-model inherits directly from TicketBase. + """ + + model: str = None + """name of the model to test""" + + route_name = 'v2:accounting:_api_v2_asset_sub' + + + + @classmethod + def setUpTestData(self): + + self.kwargs = { + 'ticket_model': self.model._meta.sub_model_type + } + + super().setUpTestData() + + + +class AssetBaseViewsetTest( + AssetBaseViewsetTestCases, + TestCase, +): + + kwargs = {} + + route_name = 'v2:accounting:_api_v2_asset' + + viewset = NoDocsViewSet From 6c91cb008c2b12b9e022de10a1536968badf3ef0 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 04:35:09 +0930 Subject: [PATCH 015/175] chore(itam): just add this fucking thing cause vscode is a cunt and wont pick up the test correctly as its using an old value that does not exist even though the fucking code works directly from the console via pytest. ref: #741 #692 --- app/accounting/tests/__init__.py | 0 app/app/tests/unit/test_unit_models.py | 7 +++++++ app/conftest.py | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 app/accounting/tests/__init__.py diff --git a/app/accounting/tests/__init__.py b/app/accounting/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/app/tests/unit/test_unit_models.py b/app/app/tests/unit/test_unit_models.py index 2611e616..22567fa7 100644 --- a/app/app/tests/unit/test_unit_models.py +++ b/app/app/tests/unit/test_unit_models.py @@ -606,6 +606,13 @@ class PyTestTenancyObjectInheritedCases( ): + parameterized_fields: dict = { + "model_notes": { + 'field_type': fields.TextField, + 'field_parameter_verbose_name_type': str + } + } + def test_create_validation_exception_no_organization(self): """ Tenancy objects must have an organization diff --git a/app/conftest.py b/app/conftest.py index 5a5c4068..c954354f 100644 --- a/app/conftest.py +++ b/app/conftest.py @@ -189,7 +189,7 @@ def pytest_generate_tests(metafunc): if type(item[1][key]) is type: - ids_name += '_' + getattr(item[1][key], '__name__', 'None').lower() + ids_name += '_' + getattr(item[1][key], '__name__', 'err_generate_tests').lower() else: From e5cb0261baa45422f7bb089c4c54b95226c4629c Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:22:48 +0930 Subject: [PATCH 016/175] feat(accounting): History model for AssetBase ref: #741 #737 --- app/accounting/models/__init__.py | 1 + app/accounting/models/asset_base.py | 15 ++++++ app/accounting/models/asset_base_history.py | 53 +++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 app/accounting/models/asset_base_history.py diff --git a/app/accounting/models/__init__.py b/app/accounting/models/__init__.py index e69de29b..3a0dcd08 100644 --- a/app/accounting/models/__init__.py +++ b/app/accounting/models/__init__.py @@ -0,0 +1 @@ +from .asset_base_history import AssetBaseHistory diff --git a/app/accounting/models/asset_base.py b/app/accounting/models/asset_base.py index 7b0f626f..4747f053 100644 --- a/app/accounting/models/asset_base.py +++ b/app/accounting/models/asset_base.py @@ -356,3 +356,18 @@ class AssetBase( self.asset_type = str(related_model._meta.sub_model_type).lower().replace(' ', '_') super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) + + + + def save_history(self, before: dict, after: dict) -> bool: + + from accounting.models.asset_base_history import AssetBaseHistory + + history = super().save_history( + before = before, + after = after, + history_model = AssetBaseHistory + ) + + + return history diff --git a/app/accounting/models/asset_base_history.py b/app/accounting/models/asset_base_history.py new file mode 100644 index 00000000..b7c1b06f --- /dev/null +++ b/app/accounting/models/asset_base_history.py @@ -0,0 +1,53 @@ +from django.db import models + +from accounting.models.asset_base import AssetBase + +from core.models.model_history import ModelHistory + + + +class AssetBaseHistory( + ModelHistory +): + + + class Meta: + + db_table = 'accounting_assetbase_history' + + ordering = ModelHistory._meta.ordering + + verbose_name = 'Asset History' + + verbose_name_plural = 'Asset History' + + + model = models.ForeignKey( + AssetBase, + blank = False, + help_text = 'Model this note belongs to', + null = False, + on_delete = models.CASCADE, + related_name = 'history', + verbose_name = 'Model', + ) + + table_fields: list = [] + + page_layout: dict = [] + + + def get_object(self): + + return self + + + def get_serialized_model(self, serializer_context): + + model = None + + from accounting.serializers.asset import BaseSerializer + + model = BaseSerializer(self.model, context = serializer_context) + + return model From c13b360ca576c0d27d92c3b762f13ceb9e6e2cee Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:23:14 +0930 Subject: [PATCH 017/175] feat(accounting): Notes model for AssetBase ref: #741 #737 --- app/accounting/models/asset_base.py | 5 --- app/accounting/models/asset_base_notes.py | 47 +++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 app/accounting/models/asset_base_notes.py diff --git a/app/accounting/models/asset_base.py b/app/accounting/models/asset_base.py index 4747f053..2d295d35 100644 --- a/app/accounting/models/asset_base.py +++ b/app/accounting/models/asset_base.py @@ -337,11 +337,6 @@ class AssetBase( return kwargs - def get_url_kwargs_notes(self): - - return FeatureNotUsed - - def save(self, force_insert=False, force_update=False, using=None, update_fields=None): diff --git a/app/accounting/models/asset_base_notes.py b/app/accounting/models/asset_base_notes.py new file mode 100644 index 00000000..687a6d78 --- /dev/null +++ b/app/accounting/models/asset_base_notes.py @@ -0,0 +1,47 @@ +from django.db import models + +from accounting.models.asset_base import AssetBase + +from core.models.model_notes import ModelNotes + + + +class AssetBaseNotes( + ModelNotes +): + + + class Meta: + + db_table = 'accounting_assetbase_notes' + + ordering = ModelNotes._meta.ordering + + verbose_name = 'Asset Note' + + verbose_name_plural = 'Asset Notes' + + + model = models.ForeignKey( + AssetBase, + blank = False, + help_text = 'Model this note belongs to', + null = False, + on_delete = models.CASCADE, + related_name = 'notes', + verbose_name = 'Model', + ) + + app_namespace = 'accounting' + + table_fields: list = [] + + page_layout: dict = [] + + + def get_url_kwargs(self) -> dict: + + return { + 'model_id': self.model.pk, + 'pk': self.pk + } From d6290471ba274c3c0181d34f46d0132c89312480 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:23:33 +0930 Subject: [PATCH 018/175] feat(accounting): Notes Serializer for AssetBase ref: #741 #737 --- .../serializers/asset_base_notes.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 app/accounting/serializers/asset_base_notes.py diff --git a/app/accounting/serializers/asset_base_notes.py b/app/accounting/serializers/asset_base_notes.py new file mode 100644 index 00000000..bd667dce --- /dev/null +++ b/app/accounting/serializers/asset_base_notes.py @@ -0,0 +1,41 @@ +from accounting.models.asset_base_notes import AssetBaseNotes + +from core.serializers.model_notes import ( + ModelNoteBaseSerializer, + ModelNoteModelSerializer, + ModelNoteViewSerializer +) + + + +class AssetBaseNoteBaseSerializer(ModelNoteBaseSerializer): + + pass + + +class AssetBaseNoteModelSerializer( + ModelNoteModelSerializer +): + + + class Meta: + + model = AssetBaseNotes + + fields = ModelNoteModelSerializer.Meta.fields + [ + 'model', + ] + + read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [ + 'model', + 'content_type', + ] + + + +class AssetBaseNoteViewSerializer( + ModelNoteViewSerializer, + AssetBaseNoteModelSerializer, +): + + pass \ No newline at end of file From 37a18d3c6eaff1a59f3d0ee5726fcb3743523cda Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:24:00 +0930 Subject: [PATCH 019/175] feat(accounting): Notes Viewset for AssetBase ref: #741 #737 --- app/accounting/urls.py | 4 +- app/accounting/viewsets/asset_notes.py | 60 ++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 app/accounting/viewsets/asset_notes.py diff --git a/app/accounting/urls.py b/app/accounting/urls.py index 646b56c1..0b2ca5c7 100644 --- a/app/accounting/urls.py +++ b/app/accounting/urls.py @@ -3,7 +3,8 @@ from django.apps import apps from centurion_feature_flag.urls.routers import APIRootView, DefaultRouter from accounting.viewsets import ( - asset + asset, + asset_notes, ) @@ -42,6 +43,7 @@ asset_type_names = str(asset_type_names)[:-1] if not asset_type_names: asset_type_names = 'none' +router.register('asset/(?P[0-9]+)/notes', asset_notes.ViewSet, feature_flag = '2025-00004', basename='_api_v2_asset_note') router.register(f'asset/(?P[{asset_type_names}]+)?', asset.ViewSet, feature_flag = '2025-00004', basename='_api_v2_asset_sub') router.register('asset', asset.NoDocsViewSet, feature_flag = '2025-00004', basename='_api_v2_asset') diff --git a/app/accounting/viewsets/asset_notes.py b/app/accounting/viewsets/asset_notes.py new file mode 100644 index 00000000..19ce7371 --- /dev/null +++ b/app/accounting/viewsets/asset_notes.py @@ -0,0 +1,60 @@ +from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse + +from accounting.serializers.asset_base_notes import ( + AssetBaseNotes, + AssetBaseNoteModelSerializer, + AssetBaseNoteViewSerializer, +) + +from core.viewsets.model_notes import ModelNoteViewSet + + + +@extend_schema_view( + create=extend_schema( + summary = 'Add a note to an Asset', + description = '', + responses = { + 201: OpenApiResponse(description='created', response=AssetBaseNoteViewSerializer), + 400: OpenApiResponse(description='Validation failed.'), + 403: OpenApiResponse(description='User is missing create permissions'), + } + ), + destroy = extend_schema( + summary = 'Delete an Asset note', + description = '' + ), + list = extend_schema( + summary = 'Fetch all Asset notes', + description='', + ), + retrieve = extend_schema( + summary = 'Fetch a single Asset note', + description='', + ), + update = extend_schema(exclude = True), + partial_update = extend_schema( + summary = 'Update an Asset note', + description = '' + ), +) +class ViewSet(ModelNoteViewSet): + + model = AssetBaseNotes + + + def get_serializer_class(self): + + if ( + self.action == 'list' + or self.action == 'retrieve' + ): + + self.serializer_class = AssetBaseNoteViewSerializer + + + else: + + self.serializer_class = AssetBaseNoteModelSerializer + + return self.serializer_class From 3d7b133005681c315b6010a63c2d1e1d0b806a9f Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:24:24 +0930 Subject: [PATCH 020/175] feat(accounting): Migrations for history model for AssetBase ref: #741 #737 --- .../migrations/0002_assetbasehistory.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 app/accounting/migrations/0002_assetbasehistory.py diff --git a/app/accounting/migrations/0002_assetbasehistory.py b/app/accounting/migrations/0002_assetbasehistory.py new file mode 100644 index 00000000..d20db66c --- /dev/null +++ b/app/accounting/migrations/0002_assetbasehistory.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.8 on 2025-05-05 06:29 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounting', '0001_initial'), + ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='AssetBaseHistory', + fields=[ + ('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')), + ('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='accounting.assetbase', verbose_name='Model')), + ], + options={ + 'verbose_name': 'Asset History', + 'verbose_name_plural': 'Asset History', + 'db_table': 'accounting_assetbase_history', + 'ordering': ['-created'], + }, + bases=('core.modelhistory',), + ), + ] From ef5c6b73af12a41a5912ad9384a1aeb9d30e2eed Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:24:34 +0930 Subject: [PATCH 021/175] feat(accounting): Migrations for notes model for AssetBase ref: #741 #737 --- .../migrations/0003_assetbasenotes.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 app/accounting/migrations/0003_assetbasenotes.py diff --git a/app/accounting/migrations/0003_assetbasenotes.py b/app/accounting/migrations/0003_assetbasenotes.py new file mode 100644 index 00000000..46f7ca3e --- /dev/null +++ b/app/accounting/migrations/0003_assetbasenotes.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.8 on 2025-05-05 06:41 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounting', '0002_assetbasehistory'), + ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='AssetBaseNotes', + fields=[ + ('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')), + ('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='accounting.assetbase', verbose_name='Model')), + ], + options={ + 'verbose_name': 'Asset Note', + 'verbose_name_plural': 'Asset Notes', + 'db_table': 'accounting_assetbase_notes', + 'ordering': ['-created'], + }, + bases=('core.modelnotes',), + ), + ] From e566a5edf11ebc89158a93fc84be4c384dc54613 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:35:03 +0930 Subject: [PATCH 022/175] chore: add link to issue template for clarity of sub-model history variables ref: #741 --- .github/ISSUE_TEMPLATE/new_model.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/new_model.md b/.github/ISSUE_TEMPLATE/new_model.md index d0f29690..d2888006 100644 --- a/.github/ISSUE_TEMPLATE/new_model.md +++ b/.github/ISSUE_TEMPLATE/new_model.md @@ -52,9 +52,9 @@ Describe in detail the following: - Sub-Models **_ONLY_** - - [ ] Model class variable `history_app_label` set to correct application label + - [ ] Model class variable [`history_app_label`](https://nofusscomputing.com/projects/centurion_erp/development/models/#history) set to correct application label - - [ ] Model class variable `history_model_name` set to correct model label + - [ ] Model class variable [`history_model_name`](https://nofusscomputing.com/projects/centurion_erp/development/models/#history) set to correct model label - [ ] 📓 New [Notes model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/) created - [ ] 🆕 Model Created From 644dbc81597e387b5c421ca50018ae32fcd4ab45 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:38:41 +0930 Subject: [PATCH 023/175] feat(accounting): Add app label to kb articles for notes ref: #741 #737 --- app/assistance/models/model_knowledge_base_article.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assistance/models/model_knowledge_base_article.py b/app/assistance/models/model_knowledge_base_article.py index 5bfe8fa1..e2d45b6c 100644 --- a/app/assistance/models/model_knowledge_base_article.py +++ b/app/assistance/models/model_knowledge_base_article.py @@ -20,12 +20,14 @@ def all_models() -> list(tuple()): model_apps: list = [ 'access', + 'accounting', 'api', 'app', 'assistance', 'config_management', 'core', 'devops', + 'human_resources', 'itam', 'itim', 'project_management', From 83d7c38c38515f6f6a73b47b8b98cecfa84c5c80 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 16:58:44 +0930 Subject: [PATCH 024/175] docs(accounting): added interim ref: #741 #737 --- .../centurion_erp/development/accounting/asset.md | 9 +++++++++ docs/projects/centurion_erp/development/models.md | 8 ++++++++ docs/projects/centurion_erp/index.md | 2 ++ .../centurion_erp/user/accounting/asset.md | 9 +++++++++ .../centurion_erp/user/accounting/index.md | 14 ++++++++++++++ mkdocs.yml | 8 ++++++++ 6 files changed, 50 insertions(+) create mode 100644 docs/projects/centurion_erp/development/accounting/asset.md create mode 100644 docs/projects/centurion_erp/user/accounting/asset.md create mode 100644 docs/projects/centurion_erp/user/accounting/index.md diff --git a/docs/projects/centurion_erp/development/accounting/asset.md b/docs/projects/centurion_erp/development/accounting/asset.md new file mode 100644 index 00000000..a1e557d6 --- /dev/null +++ b/docs/projects/centurion_erp/development/accounting/asset.md @@ -0,0 +1,9 @@ +--- +title: Asset +description: Asset Base Model Development Documentation for Centurion ERP by No Fuss Computing +date: 2025-05-05 +template: project.html +about: https://github.com/nofusscomputing/centurion_erp +--- + +Asset is a base model of Centurion ERP and contains all of the core features. This allows for easier extensibility. diff --git a/docs/projects/centurion_erp/development/models.md b/docs/projects/centurion_erp/development/models.md index 989bdc23..6f9641e6 100644 --- a/docs/projects/centurion_erp/development/models.md +++ b/docs/projects/centurion_erp/development/models.md @@ -67,6 +67,14 @@ Within Centurion ERP there are two types of models, they are: - Sub-Model +Within Centurion ERP there are some base models that are designed to be used for extending Centurion. They are: + +- [Asset](./accounting/asset.md) + +- [Entity](./core/entity.md) + +Using a base model reduces the effort required to add a feature. This is such due to how the base model / sub-model has been designed. That is the base model has the core features so you don't need to add them. All that is required for the extension is that you add your fields and test the differences. + ### Standard Model diff --git a/docs/projects/centurion_erp/index.md b/docs/projects/centurion_erp/index.md index d9f0eedb..2406cf32 100644 --- a/docs/projects/centurion_erp/index.md +++ b/docs/projects/centurion_erp/index.md @@ -31,6 +31,8 @@ Whilst there are many Enterprise Rescource Planning (ERP) applications, Centurio Centurion ERP contains the following modules: +- [Accounting](./user/accounting/index.md) + - Change Management - [Cluster Management](./user/itim/cluster.md) diff --git a/docs/projects/centurion_erp/user/accounting/asset.md b/docs/projects/centurion_erp/user/accounting/asset.md new file mode 100644 index 00000000..b196bbd1 --- /dev/null +++ b/docs/projects/centurion_erp/user/accounting/asset.md @@ -0,0 +1,9 @@ +--- +title: Asset Management +description: Asset Management Module User Documentation for Centurion ERP by No Fuss Computing +date: 2025-05-05 +template: project.html +about: https://github.com/nofusscomputing/centurion_erp +--- + +Asset Management provides the ability to manage the lifecycle of an asset. diff --git a/docs/projects/centurion_erp/user/accounting/index.md b/docs/projects/centurion_erp/user/accounting/index.md new file mode 100644 index 00000000..05d09447 --- /dev/null +++ b/docs/projects/centurion_erp/user/accounting/index.md @@ -0,0 +1,14 @@ +--- +title: Accounting +description: Accounting Module Documentation for Centurion ERP by No Fuss Computing +date: 2025-05-05 +template: project.html +about: https://github.com/nofusscomputing/centurion_erp +--- + +Accounting is a crucial component of operations and Centurion ERP's accounting module serves the purpose of being able to manage this area. + + +## Features + +- [Asset Management](./asset.md) \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index df61c886..441f494a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -131,6 +131,8 @@ nav: - projects/centurion_erp/development/api/tests/model_views.md + - projects/centurion_erp/development/accounting/asset.md + - projects/centurion_erp/development/fields.md - projects/centurion_erp/development/forms.md @@ -169,6 +171,12 @@ nav: - projects/centurion_erp/user/access/team.md + - Accounting: + + - projects/centurion_erp/user/accounting/index.md + + - projects/centurion_erp/user/accounting/asset.md + - Assistance: - projects/centurion_erp/user/assistance/index.md From b54f3b7ab44eba4309c48ef04407f7c56f904902 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 17:22:08 +0930 Subject: [PATCH 025/175] refactor(api): Limit url pk regex to ensure the value is a number ref: #741 --- .../tests/unit/test_unit_common_viewset.py | 45 +++++++++++++++++++ app/api/viewsets/common.py | 3 ++ 2 files changed, 48 insertions(+) diff --git a/app/api/tests/unit/test_unit_common_viewset.py b/app/api/tests/unit/test_unit_common_viewset.py index 41f5013f..0c32753e 100644 --- a/app/api/tests/unit/test_unit_common_viewset.py +++ b/app/api/tests/unit/test_unit_common_viewset.py @@ -850,6 +850,51 @@ class ModelViewSetBaseCases( ) + + def test_view_attr_lookup_value_regex_exists(self): + """Attribute Test + + Attribute `lookup_value_regex` must exist + """ + + assert hasattr(self.viewset, 'lookup_value_regex') + + + def test_view_attr_lookup_value_regex_not_empty(self): + """Attribute Test + + Attribute `lookup_value_regex` must return a value + """ + + assert self.viewset.lookup_value_regex is not None + + + def test_view_attr_lookup_value_regex_type(self): + """Attribute Test + + Attribute `lookup_value_regex` must be of type list + """ + + view_set = self.viewset() + + assert ( + type(view_set.lookup_value_regex) is str + ) + + + def test_view_attr_lookup_value_regex_value(self): + """Attribute Test + + Attribute `lookup_value_regex` must have a value of `[0-9]+` as this + is used for the PK lookup which is always a number. + """ + + view_set = self.viewset() + + assert view_set.lookup_value_regex == '[0-9]+' + + + def test_view_attr_model_exists(self): """Attribute Test diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index 3b44e120..3e745285 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -634,6 +634,9 @@ class ModelViewSetBase( _Optional_, if specified, these fields can be used to filter the API response """ + lookup_value_regex = '[0-9]+' + """PK value regex""" + model: object = None """Django Model _Mandatory_, Django model used for this view. From b7cd9ea75cc2b0efa710945d28a46bab083eefdd Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 17:33:30 +0930 Subject: [PATCH 026/175] fix(itim): Ensure that itam base model is always imported ref: #741 #692 --- app/itam/models/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/itam/models/__init__.py b/app/itam/models/__init__.py index d905c8e8..624d775f 100644 --- a/app/itam/models/__init__.py +++ b/app/itam/models/__init__.py @@ -1 +1,2 @@ from .device_software_history import DeviceSoftwareHistory +from .itam_asset_base import ITAMAssetBase From dbaff89b8da1f5d84833cce4cfb02d4261be9893 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 17:49:17 +0930 Subject: [PATCH 027/175] test(api): Ensure that model notes is added to model create for api field tests ref: #741 --- app/api/tests/unit/test_unit_api_fields.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/api/tests/unit/test_unit_api_fields.py b/app/api/tests/unit/test_unit_api_fields.py index 55c54a34..33adff44 100644 --- a/app/api/tests/unit/test_unit_api_fields.py +++ b/app/api/tests/unit/test_unit_api_fields.py @@ -109,6 +109,14 @@ class APIFieldsTestCases: 'organization': request.cls.organization }) + + if 'model_notes' in self.model().fields: + + request.cls.kwargs_create_item.update({ + 'model_notes': 'notes', + }) + + view_permissions = Permission.objects.get( codename = 'view_' + request.cls.model._meta.model_name, content_type = ContentType.objects.get( From 6841b30a77c3e8f7d7895d21983dac54cfc15678 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 19:32:57 +0930 Subject: [PATCH 028/175] test: Support variables that were defined as properties. ref: #741 --- app/api/tests/unit/test_unit_api_fields.py | 101 +++++++++--------- app/conftest.py | 4 + .../centurion_erp/development/testing.md | 47 ++++++++ 3 files changed, 104 insertions(+), 48 deletions(-) diff --git a/app/api/tests/unit/test_unit_api_fields.py b/app/api/tests/unit/test_unit_api_fields.py index 33adff44..82249c16 100644 --- a/app/api/tests/unit/test_unit_api_fields.py +++ b/app/api/tests/unit/test_unit_api_fields.py @@ -14,57 +14,60 @@ from app.tests.common import DoesNotExist class APIFieldsTestCases: - - api_fields_common = { - 'id': { - 'expected': int - }, - 'display_name': { - 'expected': str - }, - '_urls': { - 'expected': dict - }, - '_urls._self': { - 'expected': str - }, - '_urls.notes': { - 'expected': str - }, - } - api_fields_model = { - 'model_notes': { - 'expected': str - }, - 'created': { - 'expected': str - }, - 'modified': { - 'expected': str - }, - } + @property + def parameterized_test_data(self) -> dict: - api_fields_tenancy = { - 'organization': { - 'expected': dict - }, - 'organization.id': { - 'expected': int - }, - 'organization.display_name': { - 'expected': str - }, - 'organization.url': { - 'expected': Hyperlink - }, - } + api_fields_common = { + 'id': { + 'expected': int + }, + 'display_name': { + 'expected': str + }, + '_urls': { + 'expected': dict + }, + '_urls._self': { + 'expected': str + }, + '_urls.notes': { + 'expected': str + }, + } - parameterized_test_data = { - **api_fields_common, - **api_fields_tenancy, - **api_fields_model, - } + api_fields_model = { + 'model_notes': { + 'expected': str + }, + 'created': { + 'expected': str + }, + 'modified': { + 'expected': str + }, + } + + api_fields_tenancy = { + 'organization': { + 'expected': dict + }, + 'organization.id': { + 'expected': int + }, + 'organization.display_name': { + 'expected': str + }, + 'organization.url': { + 'expected': Hyperlink + }, + } + + return { + **api_fields_common.copy(), + **api_fields_tenancy.copy(), + **api_fields_model.copy(), + } url_view_kwargs = {} @@ -229,3 +232,5 @@ class APIFieldsInheritedCases( ): model = None + + parameterized_test_data = {} \ No newline at end of file diff --git a/app/conftest.py b/app/conftest.py index c954354f..219a4ef3 100644 --- a/app/conftest.py +++ b/app/conftest.py @@ -94,6 +94,10 @@ def pytest_generate_tests(metafunc): base_values = getattr(base, 'parameterized_' + parameterized_key, None) + if isinstance(base_values, property): + + base_values = getattr(base(), 'parameterized_' + parameterized_key, None) + if not isinstance(base_values, dict): continue diff --git a/docs/projects/centurion_erp/development/testing.md b/docs/projects/centurion_erp/development/testing.md index 8b32e361..101fea45 100644 --- a/docs/projects/centurion_erp/development/testing.md +++ b/docs/projects/centurion_erp/development/testing.md @@ -85,6 +85,53 @@ Test Cases are to test one object and one object **only**. If the object to be t !!! tip If you inherit from an `InheritedCases` Class and there is a `class_setup` fixture, don't forget to import this into your test suite. This ensures it's available for use when running tests +!!! tip + If you find that a base classes variables are being mutated by other test classes, setup the variable within the base class as a property that contains the defaults as a variable within the function and returns the data as if the property was defined as a variable + + + Don't do this as `my_variable` will be mutated by other test classes that inherit the base class. + + ``` py + + class MyTestClassBase: + + my_variable = 'a' + + + + class MyTestClass( + MyTestClassBase + ): + + my_variable = 'b' + + ``` + + Instead, do this. now `MyTestClass` wont override variable `my_variable` which means when another test class inherits from `MyTestClassBase`, variable `my_variable` will always return the desired default value. + + ``` py + + class MyTestClassBase: + + @property + def my_variable(self): + + default = 'a' + + return default.copy() + + + + class MyTestClass( + MyTestClassBase + ): + + my_variable = 'b' + + ``` + + + ### Fixtures From 0a5202984053fecb8bf9ec102b51c5c9c86f0aa6 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 20:44:56 +0930 Subject: [PATCH 029/175] test(accounting): Unit API Fields checks for AssetBase Model ref: #741 #737 --- .../test_unit_asset_base_api_fields.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 app/accounting/tests/unit/asset_base/test_unit_asset_base_api_fields.py diff --git a/app/accounting/tests/unit/asset_base/test_unit_asset_base_api_fields.py b/app/accounting/tests/unit/asset_base/test_unit_asset_base_api_fields.py new file mode 100644 index 00000000..18bacdc0 --- /dev/null +++ b/app/accounting/tests/unit/asset_base/test_unit_asset_base_api_fields.py @@ -0,0 +1,79 @@ +import pytest + +from accounting.models.asset_base import AssetBase + +from api.tests.unit.test_unit_api_fields import ( + APIFieldsInheritedCases, +) + + + +class AssetBaseAPITestCases( + APIFieldsInheritedCases, +): + + base_model = AssetBase + + + @pytest.fixture( scope = 'class') + def setup_model(self, request, + model, + ): + + if model != self.base_model: + + request.cls.url_view_kwargs.update({ + 'asset_model': model._meta.sub_model_type, + }) + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_pre, + setup_model, + create_model, + setup_post, + ): + + pass + + + parameterized_test_data = { + 'asset_number': { + 'expected': str + }, + 'serial_number': { + 'expected': str + }, + 'asset_type': { + 'expected': str + } + } + + kwargs_create_item: dict = { + 'asset_number': '123123', + 'serial_number': '65756756756', + } + + url_ns_name = 'accounting:_api_v2_asset' + """Url namespace (optional, if not required) and url name""" + + + +class AssetBaseAPIInheritedCases( + AssetBaseAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + url_ns_name = 'accounting:_api_v2_asset_sub' + + + +class AssetBaseAPIPyTest( + AssetBaseAPITestCases, +): + + pass From 4a09463f0a3552d7fed6089f2f5c3da61706b32c Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 20:45:30 +0930 Subject: [PATCH 030/175] test(itam): Unit API Fields checks for ITAM AssetBase Model ref: #741 #692 --- app/itam/models/itam_asset_base.py | 5 +-- app/itam/serializers/asset_it_asset.py | 7 ++-- .../test_unit_itam_asset_base_api_fields.py | 36 +++++++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_api_fields.py diff --git a/app/itam/models/itam_asset_base.py b/app/itam/models/itam_asset_base.py index b9a9086c..313cf4b1 100644 --- a/app/itam/models/itam_asset_base.py +++ b/app/itam/models/itam_asset_base.py @@ -16,9 +16,11 @@ class ITAMAssetBase( **Don't** use this model directly, it should be used via a sub-model. """ - app_namespace = None + note_basename = 'accounting:_api_v2_asset_note' + + class Meta: ordering = [ @@ -85,7 +87,6 @@ class ITAMAssetBase( ) - # this model uses a custom page layout page_layout: list = [] table_fields: list = [ diff --git a/app/itam/serializers/asset_it_asset.py b/app/itam/serializers/asset_it_asset.py index 82f201c7..b70c9644 100644 --- a/app/itam/serializers/asset_it_asset.py +++ b/app/itam/serializers/asset_it_asset.py @@ -34,14 +34,11 @@ class ModelSerializer( 'id', 'display_name', 'organization', + 'model_notes', + 'asset_type', 'itam_type', 'asset_number', 'serial_number', - - # 'status', - - # 'category', - 'created', 'modified', '_urls', diff --git a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_api_fields.py b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_api_fields.py new file mode 100644 index 00000000..c6060e55 --- /dev/null +++ b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_api_fields.py @@ -0,0 +1,36 @@ +from accounting.tests.unit.asset_base.test_unit_asset_base_api_fields import ( + AssetBaseAPIInheritedCases +) + + + +class ITAMAssetBaseAPITestCases( + AssetBaseAPIInheritedCases, +): + + + parameterized_test_data = { + 'itam_type': { + 'expected': str + }, + } + + kwargs_create_item: dict = {} + + + +class ITAMAssetBaseAPIInheritedCases( + ITAMAssetBaseAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + + +class ITAMAssetBaseAPIPyTest( + ITAMAssetBaseAPITestCases, +): + + pass From 7168d519d13dbcfe673bb10dcaf76796d0836bb5 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 22:07:56 +0930 Subject: [PATCH 031/175] test(api): Add submodel url resolution for metadata ref: #741 --- app/api/react_ui_metadata.py | 11 +++++++++++ app/api/tests/unit/test_unit_api_fields.py | 2 ++ .../tests/unit/test_unit_common_viewset.py | 19 ++++++++++--------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index 70444a2b..1b687ab0 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -91,6 +91,17 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): app_namespace = view.model().get_app_namespace() + base_model = getattr(view, 'base_model', None) + + if base_model is not None: + + if( + base_model.app_namespace != '' + and base_model.app_namespace is not None + ): + + app_namespace = base_model.app_namespace + ':' + if view.kwargs.get('pk', None) is not None: diff --git a/app/api/tests/unit/test_unit_api_fields.py b/app/api/tests/unit/test_unit_api_fields.py index 82249c16..93c329ce 100644 --- a/app/api/tests/unit/test_unit_api_fields.py +++ b/app/api/tests/unit/test_unit_api_fields.py @@ -82,6 +82,8 @@ class APIFieldsTestCases: organization_two ): + request.cls.url_view_kwargs = {} + with django_db_blocker.unblock(): request.cls.organization = organization_one diff --git a/app/api/tests/unit/test_unit_common_viewset.py b/app/api/tests/unit/test_unit_common_viewset.py index 0c32753e..d2611eae 100644 --- a/app/api/tests/unit/test_unit_common_viewset.py +++ b/app/api/tests/unit/test_unit_common_viewset.py @@ -350,7 +350,6 @@ class CommonViewSetCases( ): """Test Suite for class CommonViewSet""" - kwargs: dict = {} viewset = CommonViewSet @@ -358,6 +357,8 @@ class CommonViewSetCases( @classmethod def setUpTestData(self): + self.kwargs: dict = {} + if self.viewset is CommonViewSet: self.viewset.model = Organization @@ -2655,18 +2656,18 @@ class SubModelViewSetInheritedCases( viewset = None - @classmethod - def setUpTestData(self): - """Setup Test + # @classmethod + # def setUpTestData(self): + # """Setup Test - 1. make list request - """ + # 1. make list request + # """ - self.viewset.kwargs = {} + # self.viewset.kwargs = {} - self.viewset.kwargs[self.viewset.model_kwarg] = self.model._meta.sub_model_type + # self.viewset.kwargs[self.viewset.model_kwarg] = self.model._meta.sub_model_type - super().setUpTestData() + # super().setUpTestData() From 420b223ca44ed61d7bc5157cc5d243f6e7d29fc4 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 22:08:42 +0930 Subject: [PATCH 032/175] test(core): Add missing fields is_global checks for ticket base ref: #741 #723 --- app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py index a879bf87..44b43f95 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py @@ -49,6 +49,8 @@ class TicketBaseModelTestCases( }, "is_global": { 'field_type': None, + 'field_parameter_default_exists': None, + 'field_parameter_default_value': None, 'field_parameter_verbose_name_type': None }, "external_system": { From 88a30650a5db7e0c776ed978e2e8fb8de9199ae9 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 5 May 2025 22:10:50 +0930 Subject: [PATCH 033/175] test(itam): Unit Viewset checks for AssetBase Model ref: #741 #692 --- .../test_unit_asset_base_viewset.py | 19 +++---- .../test_unit_itam_asset_base_viewset.py | 53 +++++++++++++++++++ 2 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_viewset.py diff --git a/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py b/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py index b4095e6a..66cca359 100644 --- a/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py +++ b/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py @@ -19,8 +19,6 @@ class AssetBaseViewsetTestCases( model = AssetBase - kwargs = None - viewset = ViewSet base_model = AssetBase @@ -38,6 +36,12 @@ class AssetBaseViewsetTestCases( super().setUpTestData() + self.kwargs = { + 'asset_model': self.model._meta.sub_model_type + } + + self.viewset.kwargs = self.kwargs + client = Client() @@ -79,17 +83,6 @@ class AssetBaseViewsetInheritedCases( - @classmethod - def setUpTestData(self): - - self.kwargs = { - 'ticket_model': self.model._meta.sub_model_type - } - - super().setUpTestData() - - - class AssetBaseViewsetTest( AssetBaseViewsetTestCases, TestCase, diff --git a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_viewset.py b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_viewset.py new file mode 100644 index 00000000..af0f76b3 --- /dev/null +++ b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_viewset.py @@ -0,0 +1,53 @@ +from django.test import Client, TestCase + +from rest_framework.reverse import reverse + +from accounting.tests.unit.asset_base.test_unit_asset_base_viewset import ( + AssetBase, + AssetBaseViewsetInheritedCases, + NoDocsViewSet, + ViewSet, +) + + +from accounting.viewsets.asset import ( + NoDocsViewSet, + AssetBase, + ViewSet, +) + +# from api.tests.unit.test_unit_common_viewset import SubModelViewSetInheritedCases + +from itam.models.itam_asset_base import ITAMAssetBase + + + +class ITAMAssetBaseViewsetTestCases( + AssetBaseViewsetInheritedCases, +): + + model = ITAMAssetBase + + + +class ITAMAssetBaseViewsetInheritedCases( + ITAMAssetBaseViewsetTestCases, +): + """Test Suite for Sub-Models of TicketBase + + Use this Test suit if your sub-model inherits directly from TicketBase. + """ + + model: str = None + """name of the model to test""" + + route_name = 'v2:accounting:_api_v2_asset_sub' + + + +class ITAMAssetBaseViewsetTest( + ITAMAssetBaseViewsetTestCases, + TestCase, +): + + pass From 5ba243a1eab41ebecae5314da2aae936ac003c5c Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 6 May 2025 00:48:29 +0930 Subject: [PATCH 034/175] test: correct viewset tests ref: #741 --- .../test_unit_asset_base_viewset.py | 14 ++++++--- .../tests/unit/test_unit_common_viewset.py | 12 ++++++++ .../test_unit_ticket_base_viewset.py | 30 +++++++++---------- .../test_unit_itam_asset_base_model.py | 19 ++++++++++++ .../test_unit_ticket_request_viewset.py | 14 +++++++++ 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py b/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py index 66cca359..bb0f07e8 100644 --- a/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py +++ b/app/accounting/tests/unit/asset_base/test_unit_asset_base_viewset.py @@ -33,14 +33,20 @@ class AssetBaseViewsetTestCases( 1. make list request """ + self.model = AssetBase + + self.viewset = ViewSet + super().setUpTestData() - self.kwargs = { - 'asset_model': self.model._meta.sub_model_type - } + if self.model is not AssetBase: - self.viewset.kwargs = self.kwargs + self.kwargs = { + 'asset_model': self.model._meta.sub_model_type + } + + self.viewset.kwargs = self.kwargs client = Client() diff --git a/app/api/tests/unit/test_unit_common_viewset.py b/app/api/tests/unit/test_unit_common_viewset.py index d2611eae..bcce810d 100644 --- a/app/api/tests/unit/test_unit_common_viewset.py +++ b/app/api/tests/unit/test_unit_common_viewset.py @@ -811,6 +811,18 @@ class ModelViewSetBaseCases( self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True) + @classmethod + def tearDownClass(cls): + + cls.model = None + + cls.organization.delete() + + cls.view_user.delete() + + super().tearDownClass() + + def test_class_inherits_modelviewsetbase(self): """Class Inheritence check diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_viewset.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_viewset.py index d9ae16bb..9bf6e4a1 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_viewset.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_viewset.py @@ -17,9 +17,7 @@ class ViewsetTestCases( SubModelViewSetInheritedCases, ): - model = TicketBase - - kwargs = None + model = None viewset = ViewSet @@ -36,8 +34,21 @@ class ViewsetTestCases( """ + if self.model is None: + + self.model = TicketBase + super().setUpTestData() + if self.model != TicketBase: + + self.kwargs = { + 'ticket_model': self.model._meta.sub_model_type + } + + self.viewset.kwargs = self.kwargs + + client = Client() @@ -79,24 +90,11 @@ class TicketBaseViewsetInheritedCases( - @classmethod - def setUpTestData(self): - - self.kwargs = { - 'ticket_model': self.model._meta.sub_model_type - } - - super().setUpTestData() - - - class TicketBaseViewsetTest( ViewsetTestCases, TestCase, ): - kwargs = {} - route_name = 'v2:_api_v2_ticket' viewset = NoDocsViewSet diff --git a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py index 52ac956a..9302a8a4 100644 --- a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py +++ b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py @@ -59,6 +59,25 @@ class ITAMAssetBaseModelTestCases( assert self.model.app_namespace is None + def test_attribute_type_note_basename(self): + """Attribute Type + + note_basename is of type str + """ + + assert type(self.model.note_basename) is str + + + def test_attribute_value_note_basename(self): + """Attribute Type + + note_basename has been set, override this test case with the value + of attribute `note_basename` + """ + + assert self.model.note_basename == 'accounting:_api_v2_asset_note' + + class ITAMAssetBaseModelInheritedCases( ITAMAssetBaseModelTestCases, diff --git a/app/itim/tests/unit/ticket_request/test_unit_ticket_request_viewset.py b/app/itim/tests/unit/ticket_request/test_unit_ticket_request_viewset.py index 189d58df..fb78c504 100644 --- a/app/itim/tests/unit/ticket_request/test_unit_ticket_request_viewset.py +++ b/app/itim/tests/unit/ticket_request/test_unit_ticket_request_viewset.py @@ -15,6 +15,20 @@ class ViewsetTestCases( model = RequestTicket + @classmethod + def setUpTestData(self): + """Setup Test + + 1. make list request + """ + + + if self.model is None: + + self.model = RequestTicket + + super().setUpTestData() + class TicketRequestViewsetTest( ViewsetTestCases, From ed71e935fc92ec65f2409a1252b7c84af4708560 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 6 May 2025 01:43:38 +0930 Subject: [PATCH 035/175] test(api): dont use constants for variable data ref: #741 --- app/api/tests/unit/token/test_token.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/app/api/tests/unit/token/test_token.py b/app/api/tests/unit/token/test_token.py index 074bb76d..cf4cd100 100644 --- a/app/api/tests/unit/token/test_token.py +++ b/app/api/tests/unit/token/test_token.py @@ -1,18 +1,13 @@ import hashlib import json -import pytest -import requests -import unittest from datetime import datetime, timedelta -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import User from django.shortcuts import reverse from django.test import TestCase, Client from access.models.organization import Organization -from access.models.team import Team -from access.models.team_user import TeamUsers from api.models.tokens import AuthToken @@ -61,6 +56,8 @@ class APIAuthToken(TestCase): token.save() + self.token = token + self.api_token_does_not_exist = hashlib.sha256(str('a random string').encode('utf-8')).hexdigest() @@ -92,7 +89,7 @@ class APIAuthToken(TestCase): url = reverse('_user_auth_token_add', kwargs={'user_id': self.add_user.id}) - response = client.post(url, kwargs={'user_id': self.add_user.id}) + response = client.post(url) assert response.status_code == 200 @@ -109,7 +106,7 @@ class APIAuthToken(TestCase): url = reverse('_user_auth_token_add', kwargs={'user_id': 999}) - response = client.post(url, kwargs={'user_id': 999}) + response = client.post(url) assert response.status_code == 403 @@ -123,12 +120,12 @@ class APIAuthToken(TestCase): client = Client() client.force_login(self.add_user) - url = reverse('_user_auth_token_delete', kwargs={'user_id': self.add_user.id, 'pk': 1}) + url = reverse('_user_auth_token_delete', kwargs={'user_id': self.add_user.id, 'pk': self.token.id}) - response = client.post(url, kwargs={'user_id': self.add_user.id, 'pk': 1}) + response = client.post(url) - assert response.status_code == 302 and response.url == '/account/settings/1' + assert response.status_code == 302 and response.url == '/account/settings/' + str(self.add_user.id) @@ -140,10 +137,10 @@ class APIAuthToken(TestCase): client = Client() client.force_login(self.add_user) - url = reverse('_user_auth_token_delete', kwargs={'user_id': 999, 'pk': 1}) + url = reverse('_user_auth_token_delete', kwargs={'user_id': 999, 'pk': self.token.id}) - response = client.post(url, data={'id': 1}, kwargs={'user_id': 999, 'pk': 1}) + response = client.post(url, data={'id': 1}) assert response.status_code == 403 From d19bb3a20441342c9397a4e18e95cb7832998e1c Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 6 May 2025 02:07:38 +0930 Subject: [PATCH 036/175] feat(accounting): Model tag for Asset ref: #741 #737 --- app/core/lib/slash_commands/linked_model.py | 10 +++++++++- app/core/models/ticket/ticket_linked_items.py | 1 + docs/projects/centurion_erp/user/core/markdown.md | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/core/lib/slash_commands/linked_model.py b/app/core/lib/slash_commands/linked_model.py index 5eef68a5..748c079e 100644 --- a/app/core/lib/slash_commands/linked_model.py +++ b/app/core/lib/slash_commands/linked_model.py @@ -129,7 +129,15 @@ For this command to process the following conditions must be met: from core.models.ticket.ticket_linked_items import TicketLinkedItem - if model_type == 'cluster': + if model_type == 'asset': + + from accounting.models.asset_base import AssetBase + + model = AssetBase + + item_type = TicketLinkedItem.Modules.ASSET + + elif model_type == 'cluster': from itim.models.clusters import Cluster diff --git a/app/core/models/ticket/ticket_linked_items.py b/app/core/models/ticket/ticket_linked_items.py index 6884db8f..4c26e8e1 100644 --- a/app/core/models/ticket/ticket_linked_items.py +++ b/app/core/models/ticket/ticket_linked_items.py @@ -55,6 +55,7 @@ class TicketLinkedItem(TenancyObject): GIT_REPOSITORY = 15, 'Git Repository' ENTITY = 16, 'Entity' ROLE = 17, 'Role' + ASSET = 18, 'Asset' is_global = None diff --git a/docs/projects/centurion_erp/user/core/markdown.md b/docs/projects/centurion_erp/user/core/markdown.md index 031c90f4..8c3378e1 100644 --- a/docs/projects/centurion_erp/user/core/markdown.md +++ b/docs/projects/centurion_erp/user/core/markdown.md @@ -72,6 +72,7 @@ A Model link is a reference to an item within the database. Supported model link | Model | Tag | |:---|:---:| +| [asset](../accounting/asset.md) | `$asset-` | | cluster| `$cluster-` | | clustertype| `$-` | | config groups| `$config_group-` | From c8391caf07018eb37ee4335e9448f42f53d73bde Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 6 May 2025 02:08:17 +0930 Subject: [PATCH 037/175] feat(itam): Model tag for ITAsset ref: #741 #692 --- app/core/lib/slash_commands/linked_model.py | 8 ++++++++ app/core/models/ticket/ticket_linked_items.py | 1 + docs/projects/centurion_erp/user/core/markdown.md | 1 + 3 files changed, 10 insertions(+) diff --git a/app/core/lib/slash_commands/linked_model.py b/app/core/lib/slash_commands/linked_model.py index 748c079e..665be491 100644 --- a/app/core/lib/slash_commands/linked_model.py +++ b/app/core/lib/slash_commands/linked_model.py @@ -185,6 +185,14 @@ For this command to process the following conditions must be met: item_type = TicketLinkedItem.Modules.GIT_REPOSITORY + elif model_type == 'it_asset': + + from itam.models.itam_asset_base import ITAMAssetBase + + model = ITAMAssetBase + + item_type = TicketLinkedItem.Modules.IT_ASSET + elif model_type == 'kb': from assistance.models.knowledge_base import KnowledgeBase diff --git a/app/core/models/ticket/ticket_linked_items.py b/app/core/models/ticket/ticket_linked_items.py index 4c26e8e1..049acbee 100644 --- a/app/core/models/ticket/ticket_linked_items.py +++ b/app/core/models/ticket/ticket_linked_items.py @@ -56,6 +56,7 @@ class TicketLinkedItem(TenancyObject): ENTITY = 16, 'Entity' ROLE = 17, 'Role' ASSET = 18, 'Asset' + IT_ASSET = 19, 'IT Asset' is_global = None diff --git a/docs/projects/centurion_erp/user/core/markdown.md b/docs/projects/centurion_erp/user/core/markdown.md index 8c3378e1..2ea4d029 100644 --- a/docs/projects/centurion_erp/user/core/markdown.md +++ b/docs/projects/centurion_erp/user/core/markdown.md @@ -84,6 +84,7 @@ A Model link is a reference to an item within the database. Supported model link | featureflag| `$feature_flag-` | | gitrepository| `$git_repository-` | | group| `$-` | +| it_asset | `$it_asset-` | | knowledgebase| `$kb-` | | knowledgebasecategory| `$-` | | manufacturer| `$-` | From 8473d0014843b73b8680c40f76cad3c41ba18794 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 6 May 2025 02:09:44 +0930 Subject: [PATCH 038/175] feat: Model tag migration for Asset and IT Asset ref: #741 #737 #692 --- .../0023_alter_ticketlinkeditem_item_type.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app/core/migrations/0023_alter_ticketlinkeditem_item_type.py diff --git a/app/core/migrations/0023_alter_ticketlinkeditem_item_type.py b/app/core/migrations/0023_alter_ticketlinkeditem_item_type.py new file mode 100644 index 00000000..72ab01f8 --- /dev/null +++ b/app/core/migrations/0023_alter_ticketlinkeditem_item_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.8 on 2025-05-05 16:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='ticketlinkeditem', + name='item_type', + field=models.IntegerField(choices=[(1, 'Cluster'), (2, 'Config Group'), (3, 'Device'), (4, 'Operating System'), (5, 'Service'), (6, 'Software'), (7, 'Knowledge Base'), (8, 'Organization'), (9, 'Team'), (10, 'Feature Flag'), (11, 'Software Version'), (12, 'Ticket Category'), (13, 'Ticket Comment Category'), (14, 'Project State'), (15, 'Git Repository'), (16, 'Entity'), (17, 'Role'), (18, 'Asset'), (19, 'IT Asset')], help_text='Python Model location for linked item', verbose_name='Item Type'), + ), + ] From f144e0b8ef37ddf9d5c390fb773d5f01c3fd3854 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 6 May 2025 03:58:01 +0930 Subject: [PATCH 039/175] feat(itam): Add endpoint for ITAMAssetBase ref: #741 #692 --- app/api/react_ui_metadata.py | 24 ++++++++--- app/api/urls_v2.py | 4 +- app/itam/models/itam_asset_base.py | 69 +++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index 1b687ab0..8ae20682 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -85,12 +85,6 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): app_namespace = '' - if getattr(view, 'model', None): - - if getattr(view.model, 'get_app_namespace', None): - - app_namespace = view.model().get_app_namespace() - base_model = getattr(view, 'base_model', None) if base_model is not None: @@ -103,6 +97,13 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): app_namespace = base_model.app_namespace + ':' + if getattr(view, 'model', None): + + if getattr(view.model, 'get_app_namespace', None): + + app_namespace = view.model().get_app_namespace() + + if view.kwargs.get('pk', None) is not None: qs = view.get_queryset()[0] @@ -629,6 +630,17 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): } }) + if request.feature_flag['2025-00007']: + + nav['itam']['pages'] = { + 'view_itamassetbase': { + "display_name": "IT Assets", + "name": "itasset", + "link": "/itam/it_asset" + }, + **nav['itam']['pages'] + } + if request.feature_flag['2025-00006']: nav['assistance']['pages'].update({ diff --git a/app/api/urls_v2.py b/app/api/urls_v2.py index 739c22a6..14f3d0b1 100644 --- a/app/api/urls_v2.py +++ b/app/api/urls_v2.py @@ -226,8 +226,8 @@ router.register('core/(?P[a-z_]+)/(?P[0-9]+)/item_ticket', router.register('itam', itam_index_v2.Index, basename='_api_v2_itam_home') -# from accounting.viewsets import asset -# router.register('itam/(?Pdevice)?', asset.ViewSet, feature_flag = '2025-00004', basename='_api_v2_device') +from accounting.viewsets import asset +router.register('itam/(?P[it_asset]+)', asset.ViewSet, feature_flag = '2025-00007', basename='_api_v2_itam_asset') router.register('itam/device', device_v2.ViewSet, basename='_api_v2_device') router.register('itam/device/(?P[0-9]+)/operating_system', device_operating_system.ViewSet, basename='_api_v2_device_operating_system') diff --git a/app/itam/models/itam_asset_base.py b/app/itam/models/itam_asset_base.py index 313cf4b1..b79533be 100644 --- a/app/itam/models/itam_asset_base.py +++ b/app/itam/models/itam_asset_base.py @@ -1,6 +1,8 @@ from django.apps import apps from django.db import models +from rest_framework.reverse import reverse + from accounting.models.asset_base import AssetBase @@ -87,7 +89,56 @@ class ITAMAssetBase( ) - page_layout: list = [] + + page_layout: list = [ + { + "name": "Details", + "slug": "details", + "sections": [ + { + "layout": "double", + "left": [ + 'organization', + 'asset_type', + 'itam_type', + 'asset_number', + 'serial_number', + ], + "right": [ + 'model_notes', + 'created', + 'modified', + ] + } + ] + }, + { + "name": "Knowledge Base", + "slug": "kb_articles", + "sections": [ + { + "layout": "table", + "field": "knowledge_base", + } + ] + }, + { + "name": "Tickets", + "slug": "tickets", + "sections": [ + { + "layout": "table", + "field": "tickets", + } + ], + }, + { + "name": "Notes", + "slug": "notes", + "sections": [] + }, + ] + table_fields: list = [ 'id', @@ -108,6 +159,22 @@ class ITAMAssetBase( return self._meta.verbose_name + ' - ' + self.asset_number + + + def get_url( self, request = None ) -> str: + + kwargs = self.get_url_kwargs() + + url_path_name = '_api_v2_itam_asset' + + if request: + + return reverse(f"v2:{url_path_name}-detail", request=request, kwargs = kwargs ) + + + return reverse(f"v2:{url_path_name}-detail", kwargs = kwargs ) + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): From 19205bbe8b7f4e721a08b27150fa349c265db00c Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 6 May 2025 03:58:29 +0930 Subject: [PATCH 040/175] feat(itam): Add Feature Flag `2025-00007` ITAMAssetBase ref: #741 #692 --- app/app/settings.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/app/settings.py b/app/app/settings.py index d3b8800f..43a866ce 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -565,6 +565,15 @@ if FEATURE_FLAGGING_ENABLED: "created": "", "modified": "" } + }, + { + "2025-00007": { + "name": "itam.ITAMAssetBase", + "description": "ITAM Asset Base model. see https://github.com/nofusscomputing/centurion_erp/issues/692", + "enabled": True, + "created": "", + "modified": "" + } } ] From 84afc3274a3c76188fc7081170cd591b8e36a23a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 12:26:55 +0930 Subject: [PATCH 041/175] test: remove ticket only vars from api permissions tests ref: #742 #730 --- .../test_functional_api_permissions.py | 33 +++++++++++------ .../test_functional_ticket_base_permission.py | 37 +++++++++++++++++++ 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/app/api/tests/functional/test_functional_api_permissions.py b/app/api/tests/functional/test_functional_api_permissions.py index 68f77791..1915245e 100644 --- a/app/api/tests/functional/test_functional_api_permissions.py +++ b/app/api/tests/functional/test_functional_api_permissions.py @@ -516,24 +516,17 @@ class APIPermissionsInheritedCases( request.cls.kwargs_create_item.update({ 'organization': request.cls.organization, - 'opened_by': request.cls.view_user }) request.cls.kwargs_create_item_diff_org.update({ 'organization': request.cls.different_organization, - 'opened_by': request.cls.view_user }) - request.cls.other_org_item = request.cls.model.objects.create( - **request.cls.kwargs_create_item_diff_org - ) - if request.cls.add_data is not None: request.cls.add_data.update({ 'organization': request.cls.organization.id, - 'opened_by': request.cls.view_user.pk }) view_permissions = Permission.objects.get( @@ -651,11 +644,6 @@ class APIPermissionsInheritedCases( yield - request.cls.other_org_item.delete() - - del request.cls.other_org_item - - request.cls.add_user.delete() add_team.delete() @@ -679,6 +667,26 @@ class APIPermissionsInheritedCases( different_organization_team.delete() + + @pytest.fixture(scope='class', autouse = True) + def diff_org_model(self, request, django_db_blocker): + + with django_db_blocker.unblock(): + + request.cls.other_org_item = request.cls.model.objects.create( + **request.cls.kwargs_create_item_diff_org + ) + + yield request.cls.other_org_item + + with django_db_blocker.unblock(): + + request.cls.other_org_item.delete() + + del request.cls.other_org_item + + + @pytest.fixture(scope='class', autouse = True) def post_model(self, request): @@ -690,6 +698,7 @@ class APIPermissionsInheritedCases( model, var_setup, prepare, + diff_org_model, create_model, post_model ): diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_permission.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_permission.py index f929e002..b83553da 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_permission.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_permission.py @@ -39,6 +39,41 @@ class PermissionsAPITestCases( + + @pytest.fixture(scope='class') + def opened_by_var_setup(self, request): + + request.cls.kwargs_create_item.update({ + 'opened_by': request.cls.view_user + }) + + request.cls.kwargs_create_item_diff_org.update({ + 'opened_by': request.cls.view_user + }) + + if request.cls.add_data is not None: + + request.cls.add_data.update({ + 'opened_by': request.cls.view_user.pk + }) + + + + @pytest.fixture(scope='class', autouse = True) + def class_setup(self, request, django_db_blocker, + model, + var_setup, + prepare, + opened_by_var_setup, + diff_org_model, + create_model, + post_model + ): + + pass + + + def test_returned_data_from_user_and_global_organizations_only(self): """Check items returned @@ -81,7 +116,9 @@ class TicketBasePermissionsAPIInheritedCases( model, var_setup, prepare, + opened_by_var_setup, inherited_var_setup, + diff_org_model, create_model, ): From 233393e853208e156da04e9d3ee759b30bd8f611 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:38:56 +0930 Subject: [PATCH 042/175] test: add missing merge of add_data for api permissions tests ref: #742 #730 --- .../tests/functional/test_functional_api_permissions.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/api/tests/functional/test_functional_api_permissions.py b/app/api/tests/functional/test_functional_api_permissions.py index 1915245e..fcc54abb 100644 --- a/app/api/tests/functional/test_functional_api_permissions.py +++ b/app/api/tests/functional/test_functional_api_permissions.py @@ -440,7 +440,7 @@ class APIPermissionsInheritedCases( @pytest.fixture(scope='class') def var_setup(self, request): - + add_data = {} kwargs_create_item = {} kwargs_create_item_diff_org = {} @@ -451,6 +451,12 @@ class APIPermissionsInheritedCases( for base in reversed(request.cls.__mro__): + if hasattr(base, 'add_data'): + + if base.add_data is not None: + + add_data.update(**base.add_data) + if hasattr(base, 'kwargs_create_item'): if base.kwargs_create_item is not None: @@ -476,6 +482,7 @@ class APIPermissionsInheritedCases( url_view_kwargs.update(**base.url_view_kwargs) + request.cls.add_data = add_data request.cls.kwargs_create_item = kwargs_create_item request.cls.kwargs_create_item_diff_org = kwargs_create_item_diff_org request.cls.url_kwargs = url_kwargs From 324fa39b5634d64b024ff212ad0b63f186f3ac55 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:40:23 +0930 Subject: [PATCH 043/175] test(accounting): History Test Cases for AssetBase ref: #742 #737 --- .../tests/functional/asset_base/conftest.py | 24 ++++++++++++ .../test_functional_asset_base_history.py | 38 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 app/accounting/tests/functional/asset_base/conftest.py create mode 100644 app/accounting/tests/functional/asset_base/test_functional_asset_base_history.py diff --git a/app/accounting/tests/functional/asset_base/conftest.py b/app/accounting/tests/functional/asset_base/conftest.py new file mode 100644 index 00000000..b71e31a0 --- /dev/null +++ b/app/accounting/tests/functional/asset_base/conftest.py @@ -0,0 +1,24 @@ +import pytest + +from accounting.models.asset_base import AssetBase + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = AssetBase + + yield request.cls.model + + del request.cls.model + + + +@pytest.fixture(scope='function') +def create_serializer(): + + from accounting.serializers.asset import ModelSerializer + + + yield ModelSerializer diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_history.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_history.py new file mode 100644 index 00000000..4ff24a9b --- /dev/null +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_history.py @@ -0,0 +1,38 @@ +from django.test import TestCase + +from accounting.models.asset_base_history import AssetBase, AssetBaseHistory + +from core.tests.abstract.test_functional_history import HistoryEntriesCommon + + + +class History( + HistoryEntriesCommon, + TestCase, +): + + model = AssetBase + + history_model = AssetBaseHistory + + + @classmethod + def setUpTestData(self): + + super().setUpTestData() + + self.field_name = 'asset_number' + + + self.obj = self.model.objects.create( + organization = self.organization, + asset_number = self.field_value_original, + ) + + self.obj_delete = self.model.objects.create( + organization = self.organization, + asset_number = self.field_value_delete, + + ) + + self.call_the_banners() From 88202b57eed1585c2e42a4a90dcc7f96016efbd1 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:40:36 +0930 Subject: [PATCH 044/175] test(accounting): Functional Metadata Test Cases for AssetBase ref: #742 #737 --- .../test_functional_asset_base_metadata.py | 268 ++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py new file mode 100644 index 00000000..aea0b4b3 --- /dev/null +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py @@ -0,0 +1,268 @@ +from django.contrib.auth.models import Permission, User +from django.contrib.contenttypes.models import ContentType +from django.test import TestCase + +from access.models.organization import Organization +from access.models.team import Team +from access.models.team_user import TeamUsers + +from accounting.models.asset_base import AssetBase + +from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional + + + +class MetadataTestCases( + MetadataAttributesFunctional, +): + + add_data: dict = { + 'asset_number': 'abc', + 'serial_number': 'def', + 'model_notes': 'sdasds', + } + + app_namespace = 'v2' + + base_model = AssetBase + """Base model for this sub model + don't change or override this value + """ + + change_data = None + + delete_data = {} + + kwargs_create_item: dict = { + 'asset_number': '123', + 'serial_number': '456', + 'model_notes': 'sdasds', + } + + kwargs_create_item_diff_org: dict = { + 'asset_number': '789', + 'serial_number': '012', + 'model_notes': 'sdasds', + } + + model = AssetBase + + url_kwargs: dict = {} + + url_view_kwargs: dict = {} + + url_name = None + + + @classmethod + def setUpTestData(self): + """Setup Test + + 1. Create an organization for user and item + . create an organization that is different to item + 2. Create a team + 3. create teams with each permission: view, add, change, delete + 4. create a user per team + """ + + organization = Organization.objects.create(name='test_org') + + self.organization = organization + + self.different_organization = Organization.objects.create(name='test_different_organization') + + self.view_user = User.objects.create_user(username="test_user_view", password="password") + + self.item = self.model.objects.create( + organization = organization, + **self.kwargs_create_item + ) + + self.other_org_item = self.model.objects.create( + organization = self.different_organization, + **self.kwargs_create_item_diff_org + ) + + + self.url_view_kwargs.update({ 'pk': self.item.id }) + + if self.add_data is not None: + + self.add_data.update({ + 'organization': self.organization.id, + }) + + + view_permissions = Permission.objects.get( + codename = 'view_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + view_team = Team.objects.create( + team_name = 'view_team', + organization = organization, + ) + + view_team.permissions.set([view_permissions]) + + + + add_permissions = Permission.objects.get( + codename = 'add_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + add_team = Team.objects.create( + team_name = 'add_team', + organization = organization, + ) + + add_team.permissions.set([add_permissions]) + + + + change_permissions = Permission.objects.get( + codename = 'change_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + change_team = Team.objects.create( + team_name = 'change_team', + organization = organization, + ) + + change_team.permissions.set([change_permissions]) + + + + delete_permissions = Permission.objects.get( + codename = 'delete_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + delete_team = Team.objects.create( + team_name = 'delete_team', + organization = organization, + ) + + delete_team.permissions.set([delete_permissions]) + + + self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password") + + + TeamUsers.objects.create( + team = view_team, + user = self.view_user + ) + + self.add_user = User.objects.create_user(username="test_user_add", password="password") + TeamUsers.objects.create( + team = add_team, + user = self.add_user + ) + + self.change_user = User.objects.create_user(username="test_user_change", password="password") + TeamUsers.objects.create( + team = change_team, + user = self.change_user + ) + + self.delete_user = User.objects.create_user(username="test_user_delete", password="password") + TeamUsers.objects.create( + team = delete_team, + user = self.delete_user + ) + + + self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password") + + + different_organization_team = Team.objects.create( + team_name = 'different_organization_team', + organization = self.different_organization, + ) + + different_organization_team.permissions.set([ + view_permissions, + add_permissions, + change_permissions, + delete_permissions, + ]) + + TeamUsers.objects.create( + team = different_organization_team, + user = self.different_organization_user + ) + + + def test_sanity_is_asset_sub_model(self): + """Sanity Test + + This test ensures that the model being tested `self.model` is a + sub-model of `self.base_model`. + This test is required as the same viewset is used for all sub-models + of `AssetBase` + """ + + assert issubclass(self.model, self.base_model) + + + +class AssetBaseMetadataInheritedCases( + MetadataTestCases, +): + + model = None + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + url_name = 'accounting:_api_v2_asset_sub' + + + @classmethod + def setUpTestData(self): + + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + + self.url_kwargs = { + 'asset_model': self.model._meta.sub_model_type + } + + self.url_view_kwargs = { + 'asset_model': self.model._meta.sub_model_type + } + + super().setUpTestData() + + + +class AssetBaseMetadataTest( + MetadataTestCases, + TestCase, + +): + + url_name = 'accounting:_api_v2_asset' From 8238e97a45e6e84e488728bb592356d36204dd7c Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:40:44 +0930 Subject: [PATCH 045/175] test(accounting): Functional Permissions Test Cases for AssetBase ref: #742 #737 --- .../test_functional_asset_base_permission.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 app/accounting/tests/functional/asset_base/test_functional_asset_base_permission.py diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_permission.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_permission.py new file mode 100644 index 00000000..ea5331e1 --- /dev/null +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_permission.py @@ -0,0 +1,101 @@ +import pytest + +from api.tests.functional.test_functional_api_permissions import ( + APIPermissionsInheritedCases, +) + + + +class PermissionsAPITestCases( + APIPermissionsInheritedCases, +): + + add_data: dict = { + 'asset_number': 'abc', + 'serial_number': 'def', + 'model_notes': 'sdasds', + } + + app_namespace = 'v2' + + change_data = {'asset_number': 'xyz'} + + delete_data = {} + + kwargs_create_item: dict = { + 'asset_number': 'ghi', + 'serial_number': 'jkl', + 'model_notes': 'sdasds', + } + + kwargs_create_item_diff_org: dict = { + 'asset_number': 'mno', + 'serial_number': 'pqr', + 'model_notes': 'sdasds', + } + + url_kwargs: dict = {} + + url_name = 'accounting:_api_v2_asset' + + url_view_kwargs: dict = {} + + + + def test_returned_data_from_user_and_global_organizations_only(self): + """Check items returned + + This test case is a over-ride of a test case with the same name. + This model is not a tenancy model making this test not-applicable. + + Items returned from the query Must be from the users organization and + global ONLY! + """ + pass + + + +class AssetBasePermissionsAPIInheritedCases( + PermissionsAPITestCases, +): + + add_data: dict = None + + kwargs_create_item: dict = None + + kwargs_create_item_diff_org: dict = None + + url_name = 'accounting:_api_v2_asset_sub' + + + @pytest.fixture(scope='class') + def inherited_var_setup(self, request): + + request.cls.url_kwargs.update({ + 'asset_model': self.model._meta.sub_model_type + }) + + request.cls.url_view_kwargs.update({ + 'asset_model': self.model._meta.sub_model_type + }) + + + + @pytest.fixture(scope='class', autouse = True) + def class_setup(self, request, django_db_blocker, + model, + var_setup, + prepare, + inherited_var_setup, + diff_org_model, + create_model, + ): + + pass + + +class AssetBasePermissionsAPIPyTest( + PermissionsAPITestCases, +): + + pass From a08a43e2bda26867b367e72d2a486f4f7100efaa Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:40:51 +0930 Subject: [PATCH 046/175] test(accounting): Functional Serializer Test Cases for AssetBase ref: #742 #737 --- .../test_functional_asset_base_serializer.py | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py new file mode 100644 index 00000000..cf4fcc79 --- /dev/null +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py @@ -0,0 +1,194 @@ +import pytest + +from django.contrib.auth.models import User + + + +class MockView: + + _has_import: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has import permission. + """ + + _has_purge: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has purge permission. + """ + + _has_triage: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has triage permission. + """ + + + +class AssetBaseSerializerTestCases: + + + parameterized_test_data: dict = { + "model_notes": { + 'will_create': True, + }, + "asset_number": { + 'will_create': True, + }, + "serial_number": { + 'will_create': True, + }, + "asset_type": { + 'will_create': True, + }, + } + + valid_data: dict = { + 'asset_number': 'abc', + 'serial_number': 'def', + 'model_notes': 'sdasds', + 'asset_type': 'random', + } + """Valid data used by serializer to create object""" + + + + @pytest.fixture( scope = 'class') + def setup_data(self, + request, + model, + django_db_blocker, + organization_one, + ): + + with django_db_blocker.unblock(): + + request.cls.organization = organization_one + + valid_data = {} + + for base in reversed(request.cls.__mro__): + + if hasattr(base, 'valid_data'): + + if base.valid_data is None: + + continue + + valid_data.update(**base.valid_data) + + + if len(valid_data) > 0: + + request.cls.valid_data = valid_data + + + if 'organization' not in request.cls.valid_data: + + request.cls.valid_data.update({ + 'organization': request.cls.organization.pk + }) + + + request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password") + + + yield + + with django_db_blocker.unblock(): + + request.cls.view_user.delete() + + del request.cls.valid_data + + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_data, + ): + + pass + + + def test_serializer_valid_data(self, create_serializer): + """Serializer Validation Check + + Ensure that when creating an object with valid data, no validation + error occurs. + """ + + view_set = MockView() + + serializer = create_serializer( + context = { + 'view': view_set, + }, + data = self.valid_data + ) + + assert serializer.is_valid(raise_exception = True) + + + + def test_serializer_valid_data_missing_field_is_valid_permission_import(self, parameterized, param_key_test_data, + create_serializer, + param_value, + param_will_create, + ): + """Serializer Validation Check + + Ensure that when creating an object with a user with import permission + and with valid data, no validation error occurs. + """ + + valid_data = self.valid_data.copy() + + del valid_data[param_value] + + view_set = MockView() + + view_set._has_import = True + + serializer = create_serializer( + context = { + 'view': view_set, + }, + data = valid_data + ) + + is_valid = serializer.is_valid(raise_exception = False) + + assert ( + ( + not param_will_create + and param_will_create == is_valid + ) + or param_will_create == is_valid + ) + + + +class AssetBaseSerializerInheritedCases( + AssetBaseSerializerTestCases, +): + + parameterized_test_data: dict = None + + # create_model_serializer = None + # """Serializer to test""" + + model = None + """Model to test""" + + valid_data: dict = None + """Valid data used by serializer to create object""" + + + +class AssetBaseSerializerPyTest( + AssetBaseSerializerTestCases, +): + + parameterized_test_data: dict = None From f79f796a1458729e9a9b8a9355f29143245077ca Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:40:59 +0930 Subject: [PATCH 047/175] test(accounting): Functional ViewSet Test Cases for AssetBase ref: #742 #737 --- .../test_functional_asset_base_viewset.py | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py new file mode 100644 index 00000000..9d752602 --- /dev/null +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py @@ -0,0 +1,270 @@ +from django.contrib.auth.models import Permission, User +from django.contrib.contenttypes.models import ContentType +from django.test import TestCase + +from access.models.organization import Organization +from access.models.team import Team +from access.models.team_user import TeamUsers + +from accounting.models.asset_base import AssetBase + +from api.tests.abstract.api_serializer_viewset import SerializersTestCases + + + +class ViewSetBase: + + add_data: dict = { + 'asset_number': 'abc', + 'serial_number': 'def', + 'model_notes': 'sdasds', + } + + app_namespace = 'v2' + + base_model = AssetBase + """Base model for this sub model + don't change or override this value + """ + + change_data = None + + delete_data = {} + + kwargs_create_item: dict = { + 'asset_number': '123', + 'serial_number': '456', + 'model_notes': 'sdasds', + } + + kwargs_create_item_diff_org: dict = { + 'asset_number': 'mno', + 'serial_number': 'pqr', + 'model_notes': 'sdasds', + } + + model = None + + url_kwargs: dict = {} + + url_view_kwargs: dict = {} + + url_name = None + + + @classmethod + def setUpTestData(self): + """Setup Test + + 1. Create an organization for user and item + . create an organization that is different to item + 2. Create a team + 3. create teams with each permission: view, add, change, delete + 4. create a user per team + """ + + organization = Organization.objects.create(name='test_org') + + self.organization = organization + + self.different_organization = Organization.objects.create(name='test_different_organization') + + self.view_user = User.objects.create_user(username="test_user_view", password="password") + + self.item = self.model.objects.create( + organization = organization, + **self.kwargs_create_item + ) + + self.other_org_item = self.model.objects.create( + organization = self.different_organization, + **self.kwargs_create_item_diff_org + ) + + + self.url_view_kwargs.update({ 'pk': self.item.id }) + + if self.add_data is not None: + + self.add_data.update({ + 'organization': self.organization.id, + }) + + + view_permissions = Permission.objects.get( + codename = 'view_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + view_team = Team.objects.create( + team_name = 'view_team', + organization = organization, + ) + + view_team.permissions.set([view_permissions]) + + + + add_permissions = Permission.objects.get( + codename = 'add_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + add_team = Team.objects.create( + team_name = 'add_team', + organization = organization, + ) + + add_team.permissions.set([add_permissions]) + + + + change_permissions = Permission.objects.get( + codename = 'change_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + change_team = Team.objects.create( + team_name = 'change_team', + organization = organization, + ) + + change_team.permissions.set([change_permissions]) + + + + delete_permissions = Permission.objects.get( + codename = 'delete_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + delete_team = Team.objects.create( + team_name = 'delete_team', + organization = organization, + ) + + delete_team.permissions.set([delete_permissions]) + + + self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password") + + + TeamUsers.objects.create( + team = view_team, + user = self.view_user + ) + + self.add_user = User.objects.create_user(username="test_user_add", password="password") + TeamUsers.objects.create( + team = add_team, + user = self.add_user + ) + + self.change_user = User.objects.create_user(username="test_user_change", password="password") + TeamUsers.objects.create( + team = change_team, + user = self.change_user + ) + + self.delete_user = User.objects.create_user(username="test_user_delete", password="password") + TeamUsers.objects.create( + team = delete_team, + user = self.delete_user + ) + + + self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password") + + + different_organization_team = Team.objects.create( + team_name = 'different_organization_team', + organization = self.different_organization, + ) + + different_organization_team.permissions.set([ + view_permissions, + add_permissions, + change_permissions, + delete_permissions, + ]) + + TeamUsers.objects.create( + team = different_organization_team, + user = self.different_organization_user + ) + + + def test_sanity_is_asset_sub_model(self): + """Sanity Test + + This test ensures that the model being tested `self.model` is a + sub-model of `self.base_model`. + This test is required as the same viewset is used for all sub-models + of `AssetBase` + """ + + assert issubclass(self.model, self.base_model) + + + +class ViewSetTestCases( + ViewSetBase, + SerializersTestCases, +): + + model = AssetBase + + + +class AssetBaseViewSetInheritedCases( + ViewSetTestCases, +): + + model = None + + url_name = 'accounting:_api_v2_asset_sub' + + + @classmethod + def setUpTestData(self): + + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + + self.url_kwargs = { + 'asset_model': self.model._meta.sub_model_type + } + + self.url_view_kwargs = { + 'asset_model': self.model._meta.sub_model_type + } + + super().setUpTestData() + + + +class AssetBaseViewSetTest( + ViewSetTestCases, + TestCase, +): + + url_name = 'accounting:_api_v2_asset' From acff19ad587e2c0aa0421dbb5e4aca5907928550 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:41:41 +0930 Subject: [PATCH 048/175] test(itam): Functional History Test Cases for ITAMAssetBase ref: #742 #692 --- .../test_functional_itamasset_base_history.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_history.py diff --git a/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_history.py b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_history.py new file mode 100644 index 00000000..4ff24a9b --- /dev/null +++ b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_history.py @@ -0,0 +1,38 @@ +from django.test import TestCase + +from accounting.models.asset_base_history import AssetBase, AssetBaseHistory + +from core.tests.abstract.test_functional_history import HistoryEntriesCommon + + + +class History( + HistoryEntriesCommon, + TestCase, +): + + model = AssetBase + + history_model = AssetBaseHistory + + + @classmethod + def setUpTestData(self): + + super().setUpTestData() + + self.field_name = 'asset_number' + + + self.obj = self.model.objects.create( + organization = self.organization, + asset_number = self.field_value_original, + ) + + self.obj_delete = self.model.objects.create( + organization = self.organization, + asset_number = self.field_value_delete, + + ) + + self.call_the_banners() From ecb1c211791780d65a83d5af660b502d4fcd3f62 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:41:49 +0930 Subject: [PATCH 049/175] test(itam): Functional Metadata Test Cases for ITAMAssetBase ref: #742 #692 --- ...test_functional_itamasset_base_metadata.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_metadata.py diff --git a/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_metadata.py b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_metadata.py new file mode 100644 index 00000000..a8f68f76 --- /dev/null +++ b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_metadata.py @@ -0,0 +1,47 @@ +from django.test import TestCase + +from accounting.tests.functional.asset_base.test_functional_asset_base_metadata import AssetBaseMetadataInheritedCases + +from itam.models.itam_asset_base import ITAMAssetBase + + + +class MetadataTestCases( + AssetBaseMetadataInheritedCases, +): + + add_data: dict = {} + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + model = ITAMAssetBase + + url_kwargs: dict = {} + + url_view_kwargs: dict = {} + + url_name = '_api_v2_itam_asset' + + + +class ITAMAssetBaseMetadataInheritedCases( + MetadataTestCases, +): + + model = None + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + + +class ITAMAssetBaseMetadataTest( + MetadataTestCases, + TestCase, + +): + + pass From b69e54f1e9d4e95824f2abf5fd7829feb9b8d057 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:41:56 +0930 Subject: [PATCH 050/175] test(itam): Functional Permissions Test Cases for ITAMAssetBase ref: #742 #692 --- ...st_functional_itamasset_base_permission.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_permission.py diff --git a/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_permission.py b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_permission.py new file mode 100644 index 00000000..4b6a2000 --- /dev/null +++ b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_permission.py @@ -0,0 +1,47 @@ +from accounting.tests.functional.asset_base.test_functional_asset_base_permission import AssetBasePermissionsAPIInheritedCases + + + +class PermissionsAPITestCases( + AssetBasePermissionsAPIInheritedCases, +): + + add_data: dict = {} + + change_data = {} + + delete_data = {} + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + url_kwargs: dict = { + 'asset_model': 'it_asset', + } + + url_name = '_api_v2_itam_asset' + + url_view_kwargs: dict = { + 'asset_model': 'it_asset', + } + + + +class ITAMAssetBasePermissionsAPIInheritedCases( + PermissionsAPITestCases, +): + + add_data: dict = None + + kwargs_create_item: dict = None + + kwargs_create_item_diff_org: dict = None + + + +class ITAMAssetBasePermissionsAPIPyTest( + PermissionsAPITestCases, +): + + pass From e0cbf1447f8a870c9fb4c76a13868ef64d608b7e Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:42:06 +0930 Subject: [PATCH 051/175] test(itam): Functional Serializer Test Cases for ITAMAssetBase ref: #742 #692 --- ...st_functional_itamasset_base_serializer.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_serializer.py diff --git a/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_serializer.py b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_serializer.py new file mode 100644 index 00000000..d53aed12 --- /dev/null +++ b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_serializer.py @@ -0,0 +1,61 @@ + +from accounting.tests.functional.asset_base.test_functional_asset_base_serializer import AssetBaseSerializerInheritedCases + + + +class MockView: + + _has_import: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has import permission. + """ + + _has_purge: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has purge permission. + """ + + _has_triage: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has triage permission. + """ + + + +class ITAMAssetBaseSerializerTestCases( + AssetBaseSerializerInheritedCases +): + + + parameterized_test_data: dict = {} + + valid_data: dict = {} + """Valid data used by serializer to create object""" + + + +class ITAMAssetBaseSerializerInheritedCases( + ITAMAssetBaseSerializerTestCases, +): + + parameterized_test_data: dict = None + + create_model_serializer = None + """Serializer to test""" + + model = None + """Model to test""" + + valid_data: dict = None + """Valid data used by serializer to create object""" + + + +class ITAMAssetBaseSerializerPyTest( + ITAMAssetBaseSerializerTestCases, +): + + pass From a71b5e6aba827bac7405bdeea45bed9ca8d14232 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 13:42:14 +0930 Subject: [PATCH 052/175] test(itam): Functional ViewSet Test Cases for ITAMAssetBase ref: #742 #692 --- .../functional/itamasset_base/conftest.py | 24 ++++++++++ .../test_functional_itamasset_base_viewset.py | 48 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 app/itam/tests/functional/itamasset_base/conftest.py create mode 100644 app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_viewset.py diff --git a/app/itam/tests/functional/itamasset_base/conftest.py b/app/itam/tests/functional/itamasset_base/conftest.py new file mode 100644 index 00000000..8a12f4c4 --- /dev/null +++ b/app/itam/tests/functional/itamasset_base/conftest.py @@ -0,0 +1,24 @@ +import pytest + +from itam.models.itam_asset_base import ITAMAssetBase + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = ITAMAssetBase + + yield request.cls.model + + del request.cls.model + + + +@pytest.fixture(scope='function') +def create_serializer(): + + from itam.serializers.asset_it_asset import ModelSerializer + + + yield ModelSerializer diff --git a/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_viewset.py b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_viewset.py new file mode 100644 index 00000000..1638cbf4 --- /dev/null +++ b/app/itam/tests/functional/itamasset_base/test_functional_itamasset_base_viewset.py @@ -0,0 +1,48 @@ +from django.test import TestCase + +from accounting.tests.functional.asset_base.test_functional_asset_base_viewset import AssetBaseViewSetInheritedCases + +from itam.models.itam_asset_base import ITAMAssetBase + + + +class ViewSetTestCases( + AssetBaseViewSetInheritedCases +): + + add_data: dict = {} + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + model = ITAMAssetBase + + url_kwargs: dict = { + 'asset_model': 'it_asset', + } + + url_view_kwargs: dict = { + 'asset_model': 'it_asset', + } + + url_name = '_api_v2_itam_asset' + + + +class ITAMAssetBaseViewSetInheritedCases( + ViewSetTestCases, +): + + model = None + + url_name = None + + + +class ITAMAssetBaseViewSetTest( + ViewSetTestCases, + TestCase, +): + + pass From 46c4fe951684282c903d1c956fc8f8f42675a8c7 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 14:25:47 +0930 Subject: [PATCH 053/175] fix(itam): ensure RO field asset_type is set ref: #742 #692 --- app/itam/serializers/asset_it_asset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/itam/serializers/asset_it_asset.py b/app/itam/serializers/asset_it_asset.py index b70c9644..db9e3191 100644 --- a/app/itam/serializers/asset_it_asset.py +++ b/app/itam/serializers/asset_it_asset.py @@ -47,6 +47,7 @@ class ModelSerializer( read_only_fields = [ 'id', 'display_name', + 'asset_type', 'itam_type', 'created', 'modified', From 9bd9652b3d5dc11785fba08aa8d4dd843be108b6 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 14:47:56 +0930 Subject: [PATCH 054/175] fix(accounting): Ensure correct sub-model check is conducted within model type ref: #742 #737 --- app/accounting/models/asset_base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/accounting/models/asset_base.py b/app/accounting/models/asset_base.py index 2d295d35..d58da083 100644 --- a/app/accounting/models/asset_base.py +++ b/app/accounting/models/asset_base.py @@ -118,13 +118,13 @@ class AssetBase( None: The ticket is for the Base class. Used to prevent creating a base ticket. """ - ticket_type = str(self._meta.sub_model_type).lower().replace(' ', '_') + sub_model_type = str(self._meta.sub_model_type).lower().replace(' ', '_') - if ticket_type == 'ticket': + if sub_model_type == 'asset': return None - return ticket_type + return sub_model_type def get_model_type_choices(): From 40b4bb0a3e55cd868a108421ea6221b402e880d5 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 14:48:43 +0930 Subject: [PATCH 055/175] test(accounting): Remaining Unit Model test cases for AssetBase ref: #742 #737 --- .../asset_base/test_unit_asset_base_model.py | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py b/app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py index 1c5e3896..b21d6139 100644 --- a/app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py +++ b/app/accounting/tests/unit/asset_base/test_unit_asset_base_model.py @@ -135,6 +135,38 @@ class AssetBaseModelTestCases( assert self.model.app_namespace == 'accounting' + def test_function_is_property_get_model_type(self): + """Function test + + Confirm function `get_model_type` is a property + """ + + assert type(self.model.get_model_type) is property + + + def test_function_value_get_model_type(self): + """Function test + + Confirm function `get_model_type` returns None for base model + """ + + assert self.item.get_model_type is None + + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is of the sub-model type + """ + + assert type(self.item.get_related_model()) == self.model + + + def test_function_value_get_url(self): + + assert self.item.get_url() == '/api/v2/accounting/asset/' + str(self.item.id) + + class AssetBaseModelInheritedCases( AssetBaseModelTestCases, @@ -156,9 +188,26 @@ class AssetBaseModelInheritedCases( """ + def test_function_value_get_model_type(self): + """Function test + + Confirm function `get_model_type` does not have a value of None + value should be equaul to Meta.sub_model_type + """ + + assert self.item.get_model_type == self.item._meta.sub_model_type + + class AssetBaseModelPyTest( AssetBaseModelTestCases, ): - pass + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is None for base model + """ + + assert self.item.get_related_model() is None From b9ac588f876644b22fa5ba69c03a0f95610ccf0b Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 14:49:52 +0930 Subject: [PATCH 056/175] test(itam): Remaining Unit Model test cases for AssetBase ref: #742 #692 --- .../test_unit_itam_asset_base_model.py | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py index 9302a8a4..608aca03 100644 --- a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py +++ b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py @@ -78,6 +78,29 @@ class ITAMAssetBaseModelTestCases( assert self.model.note_basename == 'accounting:_api_v2_asset_note' + def test_function_is_property_get_itam_model_type(self): + """Function test + + Confirm function `get_itam_model_type` is a property + """ + + assert type(self.model.get_itam_model_type) is property + + + def test_function_value_get_itam_model_type(self): + """Function test + + Confirm function `get_itam_model_type` is a property + """ + + assert self.item.get_itam_model_type is None + + + def test_function_value_get_url(self): + + assert self.item.get_url() == '/api/v2/itam/it_asset/' + str(self.item.id) + + class ITAMAssetBaseModelInheritedCases( ITAMAssetBaseModelTestCases, @@ -99,9 +122,24 @@ class ITAMAssetBaseModelInheritedCases( """ + def test_function_value_not_None_get_itam_model_type(self): + """Function test + + Confirm function `get_itam_model_type` is a property + """ + + assert self.item.get_itam_model_type is not None + + class ITAMAssetBaseModelPyTest( ITAMAssetBaseModelTestCases, ): - pass + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is None for base model + """ + + assert self.item.get_related_model() is None From 10586591749911cf9a00e4e415b959f69fbc373d Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 14:52:35 +0930 Subject: [PATCH 057/175] test(itam): Dont use constants where variables should be used ref: #742 --- app/app/tests/functional/user/test_user_permission_viewset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/app/tests/functional/user/test_user_permission_viewset.py b/app/app/tests/functional/user/test_user_permission_viewset.py index 27a94a3e..0ee146a9 100644 --- a/app/app/tests/functional/user/test_user_permission_viewset.py +++ b/app/app/tests/functional/user/test_user_permission_viewset.py @@ -25,10 +25,10 @@ class UserPermissionsAPI(TestCase): self.url_kwargs = {} - self.url_view_kwargs = {'pk': 1} - self.view_user = User.objects.create_user(username="test_user_view", password="password") + self.url_view_kwargs = {'pk': self.view_user.pk} + def test_view_user_anon_denied(self): From e30c08fd5b989952719970b709c986f4ef8437f4 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 15:09:28 +0930 Subject: [PATCH 058/175] test(itam): test meta attribute itam_sub_model_type for ITAMBaseModel ref: #742 #692 --- .../test_unit_itam_asset_base_model.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py index 608aca03..3e7b735c 100644 --- a/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py +++ b/app/itam/tests/unit/itam_asset_base/test_unit_itam_asset_base_model.py @@ -12,6 +12,8 @@ class ITAMAssetBaseModelTestCases( kwargs_create_item: dict = {} + it_asset_base_model = ITAMAssetBase + sub_model_type = 'itam_base' """Sub Model Type @@ -39,6 +41,36 @@ class ITAMAssetBaseModelTestCases( assert issubclass(self.model, ITAMAssetBase) + def test_attribute_meta_exists_itam_sub_model_type(self): + """Attribute check + + meta.itam_sub_model_type must exist + """ + + assert hasattr(self.model()._meta, 'itam_sub_model_type') + + + def test_sanity_is_it_asset_sub_model(self): + """Sanity Test + + This test ensures that the model being tested `self.model` is a + sub-model of `self.it_asset_base_model`. + This test is required as the same viewset is used for all sub-models + of `ITAMAssetBase` + """ + + assert issubclass(self.model, self.it_asset_base_model) + + + def test_attribute_meta_type_itam_sub_model_type(self): + """Attribute type + + meta.itam_sub_model_type must be of type str + """ + + assert type(self.model()._meta.itam_sub_model_type) is str + + def test_attribute_type_app_namespace(self): """Attribute Type From d0e18fe75a0474c9a1c8c103f3396beb77474da5 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 15:29:04 +0930 Subject: [PATCH 059/175] docs(development): Add Asset ref: #742 closes #737 --- .../development/accounting/asset.md | 25 ++++++++++++++++++- .../centurion_erp/development/models.md | 13 ++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/projects/centurion_erp/development/accounting/asset.md b/docs/projects/centurion_erp/development/accounting/asset.md index a1e557d6..dfddabe2 100644 --- a/docs/projects/centurion_erp/development/accounting/asset.md +++ b/docs/projects/centurion_erp/development/accounting/asset.md @@ -6,4 +6,27 @@ template: project.html about: https://github.com/nofusscomputing/centurion_erp --- -Asset is a base model of Centurion ERP and contains all of the core features. This allows for easier extensibility. +Asset is a base model of Centurion ERP and contains all of the core features. This allows for easier extensibility. + + +## Tests + +- Unit + + - API Fields Rendering `accounting.tests.unit.asset_base.test_unit_asset_base_api_fields.AssetBaseAPIInheritedCases` + + - Model `accounting.tests.unit.asset_base.test_unit_asset_base_model.AssetBaseModelInheritedCases` + + - ViewSet `accounting.tests.unit.asset_base.test_unit_asset_base_viewset.AssetBaseViewsetInheritedCases` + +- Functional: + + - History `accounting.tests.functional.asset_base.test_functional_asset_base_history.History` + + - Metadata `accounting.tests.functional.asset_base.test_functional_asset_base_metadata.AssetBaseMetadataInheritedCases` + + - Permission `accounting.tests.functional.asset_base.test_functional_asset_base_permission.AssetBasePermissionsAPIInheritedCases` + + - Serializer `accounting.tests.functional.asset_base.test_functional_asset_base_serializer.AssetBaseSerializerInheritedCases` + + - ViewSet `accounting.tests.functional.asset_base.test_functional_asset_base_viewset.AssetBaseViewSetInheritedCases` diff --git a/docs/projects/centurion_erp/development/models.md b/docs/projects/centurion_erp/development/models.md index 6f9641e6..269745f1 100644 --- a/docs/projects/centurion_erp/development/models.md +++ b/docs/projects/centurion_erp/development/models.md @@ -86,6 +86,19 @@ This is your typical model that you would define within any Django Application. This model is known within Django as multi-table inherited models. That is where the base model is a concrete class (not an Abstract model) and the super model inherits from the concrete base model. In this instance both models get their own database tables. +#### Available Sub-Models + +We do have some core sub-models available. There intended purpose is to serve as a single place for all items of that type. Available sub-models are: + +- [Asset](./accounting/asset.md) + +- [Ticket](./core/ticket.md) + +- Ticket Comment + +All sub-models are intended to be extended and contain the core features for ALL models. This aids in extensibility and reduces the work required to add a model. + + #### Requirements From f7c75df9be76942d550077be731b244c7b49a6dd Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 8 May 2025 15:30:11 +0930 Subject: [PATCH 060/175] docs(itam): Add IT Asset ref: #742 closes #692 --- .../development/accounting/asset.md | 6 +++- .../development/itam/it_asset.md | 32 +++++++++++++++++++ .../centurion_erp/development/models.md | 2 ++ mkdocs.yml | 2 ++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 docs/projects/centurion_erp/development/itam/it_asset.md diff --git a/docs/projects/centurion_erp/development/accounting/asset.md b/docs/projects/centurion_erp/development/accounting/asset.md index dfddabe2..0c7d3e83 100644 --- a/docs/projects/centurion_erp/development/accounting/asset.md +++ b/docs/projects/centurion_erp/development/accounting/asset.md @@ -1,5 +1,5 @@ --- -title: Asset +title: IT Asset description: Asset Base Model Development Documentation for Centurion ERP by No Fuss Computing date: 2025-05-05 template: project.html @@ -8,6 +8,10 @@ about: https://github.com/nofusscomputing/centurion_erp Asset is a base model of Centurion ERP and contains all of the core features. This allows for easier extensibility. +This model has the following sub-models available: + +- [IT Asset](../itam/it_asset.md) + ## Tests diff --git a/docs/projects/centurion_erp/development/itam/it_asset.md b/docs/projects/centurion_erp/development/itam/it_asset.md new file mode 100644 index 00000000..902b9206 --- /dev/null +++ b/docs/projects/centurion_erp/development/itam/it_asset.md @@ -0,0 +1,32 @@ +--- +title: IT Asset +description: IT Asset Base Model Development Documentation for Centurion ERP by No Fuss Computing +date: 2025-05-08 +template: project.html +about: https://github.com/nofusscomputing/centurion_erp +--- + +IT Asset is a base model of Centurion ERP and contains all of the core features. This model is also a sub-model, and that of an [asset](../accounting/asset.md). + + +## Tests + +- Unit + + - API Fields Rendering `itam.tests.unit.itamasset_base.test_unit_asset_base_api_fields.ITAMAssetBaseAPIInheritedCases` + + - Model `itam.tests.unit.itamasset_base.test_unit_asset_base_model.ITAMAssetBaseModelInheritedCases` + + - ViewSet `itam.tests.unit.itamasset_base.test_unit_asset_base_viewset.ITAMAssetBaseViewsetInheritedCases` + +- Functional: + + - History `itam.tests.functional.itamasset_base.test_functional_asset_base_history.History` + + - Metadata `itam.tests.functional.itamasset_base.test_functional_asset_base_metadata.ITAMAssetBaseMetadataInheritedCases` + + - Permission `itam.tests.functional.itamasset_base.test_functional_asset_base_permission.ITAMAssetBasePermissionsAPIInheritedCases` + + - Serializer `itam.tests.functional.itamasset_base.test_functional_asset_base_serializer.ITAMAssetBaseSerializerInheritedCases` + + - ViewSet `itam.tests.functional.itamasset_base.test_functional_asset_base_viewset.ITAMAssetBaseViewSetInheritedCases` diff --git a/docs/projects/centurion_erp/development/models.md b/docs/projects/centurion_erp/development/models.md index 269745f1..3b36a31a 100644 --- a/docs/projects/centurion_erp/development/models.md +++ b/docs/projects/centurion_erp/development/models.md @@ -92,6 +92,8 @@ We do have some core sub-models available. There intended purpose is to serve as - [Asset](./accounting/asset.md) + - [IT Asset](./itam/it_asset.md) + - [Ticket](./core/ticket.md) - Ticket Comment diff --git a/mkdocs.yml b/mkdocs.yml index 441f494a..3686e255 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -145,6 +145,8 @@ nav: - projects/centurion_erp/development/core/model_notes.md + - projects/centurion_erp/development/itam/it_asset.md + - projects/centurion_erp/development/templates.md - projects/centurion_erp/development/testing.md From d037150eb367b516b2bf4f3fc8a91d3568e9e55a Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:08:34 +0930 Subject: [PATCH 061/175] test(core): Unit Model Checks for TicketCommentBase ref: #744 #726 --- .../unit/ticket_comment_base/conftest.py | 14 + .../test_unit_ticket_comment_base_model.py | 528 ++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_base/conftest.py create mode 100644 app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py diff --git a/app/core/tests/unit/ticket_comment_base/conftest.py b/app/core/tests/unit/ticket_comment_base/conftest.py new file mode 100644 index 00000000..daaa25be --- /dev/null +++ b/app/core/tests/unit/ticket_comment_base/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from core.models.ticket_comment_base import TicketCommentBase + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = TicketCommentBase + + yield request.cls.model + + del request.cls.model diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py new file mode 100644 index 00000000..72dfbc28 --- /dev/null +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py @@ -0,0 +1,528 @@ +import pytest + +from django.contrib.auth.models import User +from django.db import models + +from rest_framework.exceptions import ValidationError + +from access.models.person import Person + +from app.tests.unit.test_unit_models import ( + PyTestTenancyObjectInheritedCases, +) + +from core.models.ticket_comment_base import TicketBase, TicketCommentBase, TicketCommentCategory + + + +class TicketCommentBaseModelTestCases( + PyTestTenancyObjectInheritedCases, +): + + base_model = TicketCommentBase + + sub_model_type = 'comment' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + kwargs_create_item: dict = { + 'parent': None, + 'ticket': '', + 'external_ref': 0, + 'external_system': TicketBase.Ticket_ExternalSystem.CUSTOM_1, + 'comment_type': sub_model_type, + 'category': '', + 'body': 'asdasdas', + 'private': False, + 'template': None, + 'source': TicketBase.TicketSource.HELPDESK, + 'user': '', + 'is_closed': True, + 'date_closed': '2025-05-08T171000', + } + + + parameterized_fields: dict = { + "is_global": { + 'field_type': None, + 'field_parameter_default_exists': None, + 'field_parameter_default_value': None, + 'field_parameter_verbose_name_type': None + }, + "model_notes": { + 'field_type': None, + 'field_parameter_default_exists': None, + 'field_parameter_default_value': None, + 'field_parameter_verbose_name_type': None + }, + "parent": { + 'field_type': models.ForeignKey, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "ticket": { + 'field_type': models.ForeignKey, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "external_ref": { + 'field_type': models.fields.IntegerField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "external_system": { + 'field_type': models.fields.IntegerField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "comment_type": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "category": { + 'field_type': models.ForeignKey, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "body": { + 'field_type': models.fields.TextField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "private": { + 'field_type': models.fields.BooleanField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': False, + 'field_parameter_verbose_name_type': str, + }, + "duration": { + 'field_type': models.fields.IntegerField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': 0, + 'field_parameter_verbose_name_type': str, + }, + "estimation": { + 'field_type': models.fields.IntegerField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': 0, + 'field_parameter_verbose_name_type': str, + }, + "template": { + 'field_type': models.ForeignKey, + 'field_parameter_default_exists': True, + 'field_parameter_verbose_name_type': str, + }, + "source": { + 'field_type': models.fields.IntegerField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': TicketBase.TicketSource.HELPDESK, + 'field_parameter_verbose_name_type': str, + }, + "user": { + 'field_type': models.ForeignKey, + 'field_parameter_default_exists': False, + 'field_parameter_default_value': None, + 'field_parameter_verbose_name_type': str, + }, + "is_closed": { + 'field_type': models.fields.BooleanField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': False, + 'field_parameter_verbose_name_type': str, + }, + "date_closed": { + 'field_type': models.fields.DateTimeField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + } + + + @pytest.fixture( scope = 'class') + def setup_model(self, + request, + model, + django_db_blocker, + organization_one, + organization_two + ): + + request.cls.model = model + + with django_db_blocker.unblock(): + + request.cls.organization = organization_one + + request.cls.different_organization = organization_two + + kwargs_create_item = {} + + for base in reversed(request.cls.__mro__): + + if hasattr(base, 'kwargs_create_item'): + + if base.kwargs_create_item is None: + + continue + + kwargs_create_item.update(**base.kwargs_create_item) + + + if len(kwargs_create_item) > 0: + + request.cls.kwargs_create_item = kwargs_create_item + + + request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password") + + comment_category = TicketCommentCategory.objects.create( + organization = request.cls.organization, + name = 'test cat comment' + ) + + ticket = TicketBase.objects.create( + organization = request.cls.organization, + title = 'tester comment ticket', + opened_by = request.cls.view_user, + ) + + user = Person.objects.create( + organization = request.cls.organization, + f_name = 'ip', + l_name = 'funny' + ) + + request.cls.kwargs_create_item.update({ + 'category': comment_category, + 'ticket': ticket, + 'user': user, + }) + + + if 'organization' not in request.cls.kwargs_create_item: + + request.cls.kwargs_create_item.update({ + 'organization': request.cls.organization + }) + + yield + + with django_db_blocker.unblock(): + + del request.cls.kwargs_create_item + + comment_category.delete() + + ticket.delete() + + user.delete() + + request.cls.view_user.delete() + + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_model, + create_model, + ): + + pass + + + + @pytest.fixture + def ticket(self, request, django_db_blocker): + + with django_db_blocker.unblock(): + + ticket = TicketBase.objects.create( + organization = request.cls.organization, + title = 'per function_ticket', + opened_by = request.cls.view_user, + ) + + yield ticket + + + with django_db_blocker.unblock(): + + for comment in ticket.ticketcommentbase_set.all(): + + comment.delete() + + ticket.delete() + + + def test_create_validation_exception_no_organization(self): + """ Tenancy objects must have an organization + + This test case is an over-ride of a test with the same name. this test + is not required as the organization is derived from the ticket. + + Must not be able to create an item without an organization + """ + + pass + + + def test_class_inherits_ticketcommentbase(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory + """ + + assert issubclass(self.model, TicketCommentBase) + + + def test_attribute_meta_exists_permissions(self): + """Attribute Check + + Ensure attribute `Meta.permissions` exists + """ + + assert hasattr(self.model._meta, 'permissions') + + + def test_attribute_meta_not_none_permissions(self): + """Attribute Check + + Ensure attribute `Meta.permissions` does not have a value of none + """ + + assert self.model._meta.permissions is not None + + + def test_attribute_meta_type_permissions(self): + """Attribute Check + + Ensure attribute `Meta.permissions` value is of type list + """ + + assert type(self.model._meta.permissions) is list + + + def test_attribute_value_permissions_has_import(self): + """Attribute Check + + Ensure attribute `Meta.permissions` value contains permission + `import` + """ + + permission_found = False + + for permission, description in self.model._meta.permissions: + + if permission == 'import_' + self.model._meta.model_name: + + permission_found = True + break + + assert permission_found + + + def test_attribute_value_permissions_has_triage(self): + """Attribute Check + + Ensure attribute `Meta.permissions` value contains permission + `triage` + """ + + permission_found = False + + for permission, description in self.model._meta.permissions: + + if permission == 'triage_' + self.model._meta.model_name: + + permission_found = True + break + + assert permission_found + + + def test_attribute_value_permissions_has_purge(self): + """Attribute Check + + Ensure attribute `Meta.permissions` value contains permission + `purge` + """ + + permission_found = False + + for permission, description in self.model._meta.permissions: + + if permission == 'purge_' + self.model._meta.model_name: + + permission_found = True + break + + assert permission_found + + + def test_attribute_meta_type_sub_model_type(self): + """Attribute Check + + Ensure attribute `Meta.sub_model_type` value is of type str + """ + + assert type(self.model._meta.sub_model_type) is str + + + def test_attribute_meta_value_sub_model_type(self): + """Attribute Check + + Ensure attribute `Meta.sub_model_type` value is correct + """ + + assert self.model._meta.sub_model_type == self.sub_model_type + + + def test_attribute_type_get_comment_type(self): + """Attribute Check + + Ensure attribute `get_comment_type` value is correct + """ + + assert self.item.get_comment_type == self.item._meta.sub_model_type + + + + def test_function_get_related_model(self): + """Function Check + + Confirm function `get_related_model` returns `None` for self + """ + + assert self.item.get_related_model() == None + + + + def test_function_get_related_field_name(self): + """Function Check + + Confirm function `get_related_field_name` returns an empty string + for self + """ + + assert self.item.get_related_field_name() == '' + + + + def test_function_get_url(self): + """Function Check + + Confirm function `get_url` returns the correct url + """ + + if self.item.parent: + + expected_value = '/core/ticket/' + str(self.item.ticket.id) + '/' + self.sub_model_type + '/' + str( + self.item.parent.id) + '/threads/' + str(self.item.id) + + else: + + expected_value = '/core/ticket/' + str( self.item.ticket.id) + '/' + self.sub_model_type + '/' + str(self.item.id) + + assert self.item.get_url() == '/api/v2' + expected_value + + + def test_function_parent_object(self): + """Function Check + + Confirm function `parent_object` returns the ticket + """ + + assert self.item.parent_object == self.item.ticket + + + def test_function_clean_validation_mismatch_comment_type_raises_exception(self): + """Function Check + + Ensure function `clean` does validation + """ + + valid_data = self.kwargs_create_item.copy() + + valid_data['comment_type'] = 'Nope' + + with pytest.raises(ValidationError) as err: + + self.model.objects.create( + **valid_data + ) + + assert err.value.get_codes()['comment_type'] == 'comment_type_wrong_endpoint' + + + + def test_function_called_clean_ticketcommentbase(self, model, mocker, ticket): + """Function Check + + Ensure function `TicketCommentBase.clean` is called + """ + + spy = mocker.spy(TicketCommentBase, 'clean') + + valid_data = self.kwargs_create_item.copy() + + valid_data['ticket'] = ticket + + del valid_data['external_system'] + del valid_data['external_ref'] + + model.objects.create( + **valid_data + ) + + assert spy.assert_called_once + + + +class TicketCommentBaseModelInheritedCases( + TicketCommentBaseModelTestCases, +): + """Sub-Ticket Test Cases + + Test Cases for Ticket models that inherit from model TicketCommentBase + """ + + kwargs_create_item: dict = {} + + model = None + + + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ + + + +class TicketCommentBaseModelPyTest( + TicketCommentBaseModelTestCases, +): + + + def test_function_clean_validation_close_raises_exception(self, ticket): + """Function Check + + Ensure function `clean` does validation + """ + + valid_data = self.kwargs_create_item.copy() + + valid_data['ticket'] = ticket + + del valid_data['date_closed'] + + with pytest.raises(ValidationError) as err: + + self.model.objects.create( + **valid_data + ) + + assert err.value.get_codes()['date_closed'] == 'ticket_closed_no_date' From 580820ef441032ec5984e8653aacee1c30c165ad Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:11:59 +0930 Subject: [PATCH 062/175] test(core): Unit Model Checks for TicketCommentSolution ref: #744 #728 --- .../unit/ticket_comment_solution/__init__.py | 0 .../unit/ticket_comment_solution/conftest.py | 14 ++++ ...test_unit_ticket_comment_solution_model.py | 84 +++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_solution/__init__.py create mode 100644 app/core/tests/unit/ticket_comment_solution/conftest.py create mode 100644 app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py diff --git a/app/core/tests/unit/ticket_comment_solution/__init__.py b/app/core/tests/unit/ticket_comment_solution/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/core/tests/unit/ticket_comment_solution/conftest.py b/app/core/tests/unit/ticket_comment_solution/conftest.py new file mode 100644 index 00000000..0b91e73a --- /dev/null +++ b/app/core/tests/unit/ticket_comment_solution/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from core.models.ticket_comment_solution import TicketCommentSolution + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = TicketCommentSolution + + yield request.cls.model + + del request.cls.model diff --git a/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py new file mode 100644 index 00000000..516688b0 --- /dev/null +++ b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py @@ -0,0 +1,84 @@ +import pytest + +from rest_framework.exceptions import ValidationError + +from core.models.ticket_comment_solution import TicketCommentSolution +from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_model import ( + TicketCommentBaseModelInheritedCases +) + + +class TicketCommentSolutionModelTestCases( + TicketCommentBaseModelInheritedCases, +): + + sub_model_type = 'solution' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + kwargs_create_item: dict = { + 'comment_type': sub_model_type, + } + + + def test_class_inherits_ticketcommentsolution(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory + """ + + assert issubclass(self.model, TicketCommentSolution) + + + + def test_function_called_clean_ticketcommentsolution(self, model, mocker, ticket): + """Function Check + + Ensure function `TicketCommentBase.clean` is called + """ + + spy = mocker.spy(TicketCommentSolution, 'clean') + + valid_data = self.kwargs_create_item.copy() + + valid_data['ticket'] = ticket + + del valid_data['external_system'] + del valid_data['external_ref'] + + model.objects.create( + **valid_data + ) + + assert spy.assert_called_once + + + +class TicketCommentSolutionModelInheritedCases( + TicketCommentSolutionModelTestCases, +): + """Sub-Ticket Test Cases + + Test Cases for Ticket models that inherit from model TicketCommentSolution + """ + + kwargs_create_item: dict = {} + + model = None + + + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ + + + +class TicketCommentSolutionModelPyTest( + TicketCommentSolutionModelTestCases, +): + + pass From 7ddb72239e86da3300555fb4f3f5849a524ce080 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:13:38 +0930 Subject: [PATCH 063/175] chore(python): Add testing dep pytest-mock ref: #744 --- requirements_test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements_test.txt b/requirements_test.txt index 1c7ccf71..4f7172f6 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,5 +1,6 @@ pytest==8.3.5 pytest-django==4.11.1 +pytest-mock==3.14.0 coverage==7.8.0 pytest-cov==6.1.1 From 45c428d30a2789866ef7fdeef521ea557733a88d Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:19:33 +0930 Subject: [PATCH 064/175] fix(core): Correct logic for TicketCommentBase ref: #744 #726 --- app/core/models/ticket_comment_base.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/core/models/ticket_comment_base.py b/app/core/models/ticket_comment_base.py index 8216bec0..642108f5 100644 --- a/app/core/models/ticket_comment_base.py +++ b/app/core/models/ticket_comment_base.py @@ -62,7 +62,6 @@ class TicketCommentBase( parent = models.ForeignKey( 'self', blank = True, - default = None, help_text = 'Parent ID for creating discussion threads', null = True, on_delete = models.PROTECT, @@ -80,7 +79,6 @@ class TicketCommentBase( external_ref = models.IntegerField( blank = True, - default = None, help_text = 'External System reference', null = True, verbose_name = 'Reference Number', @@ -89,7 +87,6 @@ class TicketCommentBase( external_system = models.IntegerField( blank = True, choices=TicketBase.Ticket_ExternalSystem, - default=None, help_text = 'External system this item derives', null=True, verbose_name = 'External System', @@ -98,7 +95,7 @@ class TicketCommentBase( @property def get_comment_type(self): - comment_type = str(self.Meta.sub_model_type).lower().replace( + comment_type = str(self._meta.sub_model_type).lower().replace( ' ', '_' ) @@ -134,7 +131,6 @@ class TicketCommentBase( category = models.ForeignKey( TicketCommentCategory, blank = True, - default = None, help_text = 'Category of the comment', null = True, on_delete = models.PROTECT, @@ -411,10 +407,3 @@ class TicketCommentBase( if hasattr(self.ticket, '_ticket_comments'): del self.ticket._ticket_comments - - # if self.comment_type == self.CommentType.SOLUTION: - - # update_ticket = self.ticket.__class__.objects.get(pk=self.ticket.id) - # update_ticket.status = int(TicketBase.TicketStatus.All.SOLVED.value) - - # update_ticket.save() \ No newline at end of file From 457d329b0bfe1ff3b76c68ea71d86ff6eaa24d97 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:19:57 +0930 Subject: [PATCH 065/175] fix(core): Correct logic for TicketCommentSolution ref: #744 #728 --- app/core/models/ticket_comment_solution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/core/models/ticket_comment_solution.py b/app/core/models/ticket_comment_solution.py index f57e7728..8056f982 100644 --- a/app/core/models/ticket_comment_solution.py +++ b/app/core/models/ticket_comment_solution.py @@ -62,6 +62,8 @@ class TicketCommentSolution( self.date_closed = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() + super().save(force_insert = force_insert, force_update = force_update, using = using, update_fields = update_fields) + self.ticket.is_solved = self.is_closed self.ticket.date_solved = self.date_closed @@ -70,8 +72,6 @@ class TicketCommentSolution( self.ticket.save() - super().save(force_insert = force_insert, force_update = force_update, using = using, update_fields = update_fields) - # clear comment cache if hasattr(self.ticket, '_ticket_comments'): From d399698eb1162e105b2a90c388b7cfc0c7733448 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:20:44 +0930 Subject: [PATCH 066/175] test(core): Unit Model assert save and call are called for TicketBase ref: #744 #723 --- .../test_unit_ticket_base_model.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py index 44b43f95..2e4f7b9c 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py @@ -850,6 +850,51 @@ class TicketBaseModelTestCases( + def test_function_called_clean_ticketcommentbase(self, model, mocker): + """Function Check + + Ensure function `TicketBase.clean` is called + """ + + spy = mocker.spy(TicketBase, 'clean') + + valid_data = self.kwargs_create_item.copy() + + valid_data['title'] = 'was clean called' + + del valid_data['external_system'] + + model.objects.create( + **valid_data + ) + + assert spy.assert_called_once + + + + def test_function_called_save_ticketcommentbase(self, model, mocker): + """Function Check + + Ensure function `TicketBase.save` is called + """ + + spy = mocker.spy(TicketBase, 'save') + + valid_data = self.kwargs_create_item.copy() + + valid_data['title'] = 'was save called' + + del valid_data['external_system'] + + model.objects.create( + **valid_data + ) + + assert spy.assert_called_once + + + + class TicketBaseModelInheritedCases( TicketBaseModelTestCases, ): From 5900c13e08f27122d45a368a0ced05d2455d583b Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:21:42 +0930 Subject: [PATCH 067/175] docs(development): Add initial TicketCommentBase ref: #744 #726 --- .../development/core/ticket_comment.md | 45 +++++++++++++++++++ .../centurion_erp/development/models.md | 2 +- mkdocs.yml | 2 + 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 docs/projects/centurion_erp/development/core/ticket_comment.md diff --git a/docs/projects/centurion_erp/development/core/ticket_comment.md b/docs/projects/centurion_erp/development/core/ticket_comment.md new file mode 100644 index 00000000..790bb31b --- /dev/null +++ b/docs/projects/centurion_erp/development/core/ticket_comment.md @@ -0,0 +1,45 @@ +--- +title: Ticket Comment +description: Centurion ERP Base Model Ticket Comment development documentation +date: 2025-04-16 +template: project.html +about: https://github.com/nofusscomputing/centurion_erp +--- + +Ticket Comments is a base model within Centurion ERP. This base provides the core features for all subsequent sub-ticket_comment models. As such extending Centurion ERP with a new ticket comment type is a simple process. The adding of a ticket comment type only requires that you extend an existing ticket model containing only the changes for your new ticket type. + + +## Core Features + +- ... + + +## History + +Ticketing does not use the standard history model of Centurion ERP. History for a ticket is kept in the form of action comments. As each change to a ticket occurs, an action comment is created denoting the from and to in relation to a change. + + +## Model + +When creating your sub-model, do not re-define any field that is already specified within the model you are inheriting from. This is however, with the exception of the code docs specifying otherwise. + + +## Testing + +As with any other object within Centurion, the addition of a feature requires it be tested. The following Test Suites are available: + +- `Unit` Test Cases + + - `core.tests.unit.ticket_comment_base.<*>.InheritedCases` _(if inheriting from `TicketCommentBase`)_ Test cases for sub-models + + - ViewSet `core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_viewset.TicketCommentBaseViewsetInheritedCases` + +- `Functional` Test Cases + + - `core.tests.functional.ticket_comment_base.<*>.InheritedCases` _(if inheriting from `TicketCommentBase`)_ Test cases for sub-models + + - API Permissions `core.tests.functional.ticket_comment_base.test_functional_ticket_comment_base_permission.TicketCommentBasePermissionsAPIInheritedCases` + + - Model `app.core.tests.functional.ticket_comment_base.test_functional_ticket_comment_base_model.TicketCommentBaseModelInheritedTestCases` _(if inheriting from `TicketCommentBase`)_ Test cases for sub-models + +The above listed test cases cover **all** tests for objects that are inherited from the base class. To complete the tests, you will need to add test cases for the differences your model introduces. diff --git a/docs/projects/centurion_erp/development/models.md b/docs/projects/centurion_erp/development/models.md index 3b36a31a..53dcf582 100644 --- a/docs/projects/centurion_erp/development/models.md +++ b/docs/projects/centurion_erp/development/models.md @@ -96,7 +96,7 @@ We do have some core sub-models available. There intended purpose is to serve as - [Ticket](./core/ticket.md) -- Ticket Comment +- [Ticket Comment](./core/ticket_comment.md) All sub-models are intended to be extended and contain the core features for ALL models. This aids in extensibility and reduces the work required to add a model. diff --git a/mkdocs.yml b/mkdocs.yml index 3686e255..e4aaa8c4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -153,6 +153,8 @@ nav: - projects/centurion_erp/development/core/ticket.md + - projects/centurion_erp/development/core/ticket_comment.md + - projects/centurion_erp/development/views.md - User: From 806ffb2754aefc011f3849595ef40cc28119f97b Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 16:25:18 +0930 Subject: [PATCH 068/175] chore(python): upgrade django 5.1.8 -> 5.1.9 ref: #744 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0e3142c7..53804067 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -Django==5.1.8 +django==5.1.9 django-cors-headers==4.4.0 django-debug-toolbar==5.1.0 From 626a5ccb11cc08436bc0f2d70163f169f329f6ec Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 20:06:45 +0930 Subject: [PATCH 069/175] refactor(access): when fetching parent object, use the parent_model get function ref: #744 --- app/access/mixins/organization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/access/mixins/organization.py b/app/access/mixins/organization.py index 308ccde1..2d29d313 100644 --- a/app/access/mixins/organization.py +++ b/app/access/mixins/organization.py @@ -130,7 +130,7 @@ class OrganizationMixin: parent_model (Model): with PK from kwargs['pk'] """ - return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg]) + return self.get_parent_model().objects.get(pk=self.kwargs[self.parent_model_pk_kwarg]) From 85b5bf7b58bb616aa62ad30abb748e7a08b452e9 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 20:08:06 +0930 Subject: [PATCH 070/175] feat(core): Do validate the comment_type field for TicketCommentBase ref: #744 #726 --- app/core/models/ticket_comment_base.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/core/models/ticket_comment_base.py b/app/core/models/ticket_comment_base.py index 642108f5..35856978 100644 --- a/app/core/models/ticket_comment_base.py +++ b/app/core/models/ticket_comment_base.py @@ -45,6 +45,19 @@ class TicketCommentBase( verbose_name_plural = "Ticket Comments" + def field_validation_not_empty(value): + + if value == '' or value is None: + + raise centurion_exception.ValidationError( + detail = { + 'comment_type': 'Comment Type requires a value.' + }, + code = 'comment_type_empty_or_null' + ) + + return True + model_notes = None @@ -125,6 +138,9 @@ class TicketCommentBase( help_text = 'Type this comment is. derived from Meta.verbose_name', max_length = 30, null = False, + validators = [ + field_validation_not_empty + ], verbose_name = 'Type', ) From eb2282efac223642edb431569f774473800b1f09 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 20:40:45 +0930 Subject: [PATCH 071/175] test(core): Unit API Fields Render for TicketCommentBase model ref: #744 #726 --- ...est_unit_ticket_comment_base_api_fields.py | 338 ++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py new file mode 100644 index 00000000..7bf2c144 --- /dev/null +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py @@ -0,0 +1,338 @@ +import pytest + +from django.contrib.auth.models import ContentType, Permission, User + +from rest_framework.relations import Hyperlink + +from app.tests.common import DoesNotExist + +from api.tests.unit.test_unit_api_fields import ( + APIFieldsInheritedCases, +) + +from core.models.ticket_comment_base import ( + Entity, + TicketBase, + TicketCommentBase, + TicketCommentCategory +) + + + +class TicketCommentBaseAPITestCases( + APIFieldsInheritedCases, +): + + base_model = TicketCommentBase + + + @pytest.fixture( scope = 'class') + def setup_model(self, request, django_db_blocker, + model, + ): + + with django_db_blocker.unblock(): + + + ticket_view_permission = Permission.objects.get( + codename = 'view_' + TicketBase._meta.model_name, + content_type = ContentType.objects.get( + app_label = TicketBase._meta.app_label, + model = TicketBase._meta.model_name, + ) + ) + + request.cls.view_team.permissions.add( ticket_view_permission ) + + + + + category = TicketCommentCategory.objects.create( + organization = request.cls.organization, + name = 'comment category' + ) + + ticket_user = User.objects.create_user(username="ticket_user", password="password") + + ticket = TicketBase.objects.create( + organization = request.cls.organization, + title = 'ticket comment title', + opened_by = ticket_user, + ) + + + comment_user = Entity.objects.create( + organization = request.cls.organization, + ) + + + valid_data = request.cls.kwargs_create_item.copy() + + valid_data['body'] = 'the parent comment' + valid_data['user'] = comment_user + valid_data['ticket'] = ticket + valid_data['comment_type'] = TicketCommentBase._meta.sub_model_type + + del valid_data['external_ref'] + del valid_data['external_system'] + del valid_data['category'] + del valid_data['parent'] + del valid_data['template'] + + parent_comment = TicketCommentBase.objects.create( + **valid_data + ) + + + valid_data['body'] = 'the template comment' + + template_comment = TicketCommentBase.objects.create( + **valid_data + ) + + + request.cls.kwargs_create_item.update({ + 'category': category, + 'ticket': ticket, + 'user': comment_user, + 'parent': parent_comment, + 'template': template_comment, + 'comment_type': model._meta.sub_model_type + }) + + + + yield + + + + with django_db_blocker.unblock(): + + parent_comment.delete() + + template_comment.delete() + + category.delete() + + + for comment in ticket.ticketcommentbase_set.all(): + + comment.delete() + + ticket.delete() + + ticket_user.delete() + + + + + + @pytest.fixture( scope = 'class') + def post_model(self, request, model ): + + request.cls.url_view_kwargs.update({ + 'ticket_id': request.cls.item.ticket.id + }) + + if ( + model != self.base_model + or self.item.parent + ): + + request.cls.url_view_kwargs.update({ + 'ticket_comment_model': model._meta.sub_model_type + }) + + if self.item.parent: + + request.cls.url_ns_name = '_api_v2_ticket_comment_base_sub_thread' + + request.cls.url_view_kwargs.update({ + 'parent_id': self.item.parent.id + }) + + + + + + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, request, django_db_blocker, + setup_pre, + setup_model, + create_model, + post_model, + setup_post, + ): + + pass + + + + @property + def parameterized_test_data(self): + + return { + + 'parent': { + 'expected': dict + }, + 'parent.id': { + 'expected': int + }, + 'parent.display_name': { + 'expected': str + }, + 'parent.url': { + 'expected': str + }, + + + 'ticket': { + 'expected': dict + }, + 'ticket.id': { + 'expected': int + }, + 'ticket.display_name': { + 'expected': str + }, + 'ticket.url': { + 'expected': str + }, + + 'external_ref': { + 'expected': int + }, + 'external_system': { + 'expected': int + }, + 'comment_type': { + 'expected': str + }, + 'category': { + 'expected': dict + }, + 'category.id': { + 'expected': int + }, + 'category.display_name': { + 'expected': str + }, + 'category.url': { + 'expected': Hyperlink + }, + + 'body': { + 'expected': str + }, + 'private': { + 'expected': bool + }, + 'duration': { + 'expected': int + }, + 'estimation': { + 'expected': int + }, + 'template': { + 'expected': dict + }, + 'template.id': { + 'expected': int + }, + 'template.display_name': { + 'expected': str + }, + 'template.url': { + 'expected': str + }, + + 'is_template': { + 'expected': bool + }, + 'source': { + 'expected': int + }, + 'user': { + 'expected': dict + }, + 'user.id': { + 'expected': int + }, + 'user.display_name': { + 'expected': str + }, + 'user.url': { + 'expected': Hyperlink + }, + + 'is_closed': { + 'expected': bool + }, + 'date_closed': { + 'expected': str + }, + + '_urls.threads': { + 'expected': str + }, + # Below fields dont exist. + + 'display_name': { + 'expected': DoesNotExist + }, + 'model_notes': { + 'expected': DoesNotExist + }, + '_urls.notes': { + 'expected': DoesNotExist + }, + } + + + + kwargs_create_item: dict = { + 'parent': '', + 'ticket': '', + 'external_ref': 123, + 'external_system': TicketBase.Ticket_ExternalSystem.CUSTOM_1, + 'comment_type': '', + 'category': '', + 'body': 'the ticket comment', + 'private': False, + 'duration': 1, + 'estimation': 2, + 'template': '', + 'is_template': True, + 'source': TicketBase.TicketSource.HELPDESK, + 'user': '', + 'is_closed': True, + 'date_closed': '2025-05-09T19:32Z', + } + + + + url_ns_name = '_api_v2_ticket_comment_base' + """Url namespace (optional, if not required) and url name""" + + + +class TicketCommentBaseAPIInheritedCases( + TicketCommentBaseAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + url_ns_name = '_api_v2_ticket_comment_base_sub' + + + +class TicketCommentBaseAPIPyTest( + TicketCommentBaseAPITestCases, +): + + pass From 4f8be435273db6bbb0c01388ad24d73581182c6e Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 20:41:26 +0930 Subject: [PATCH 072/175] test(core): Unit API Fields Render for TicketCommentSolution model ref: #744 #728 --- .../unit/ticket_comment_base/__init__.py | 0 ...st_unit_ticket_solution_base_api_fields.py | 31 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_base/__init__.py create mode 100644 app/core/tests/unit/ticket_comment_solution/test_unit_ticket_solution_base_api_fields.py diff --git a/app/core/tests/unit/ticket_comment_base/__init__.py b/app/core/tests/unit/ticket_comment_base/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_solution_base_api_fields.py b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_solution_base_api_fields.py new file mode 100644 index 00000000..22160ffd --- /dev/null +++ b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_solution_base_api_fields.py @@ -0,0 +1,31 @@ +from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_api_fields import ( + TicketCommentBaseAPIInheritedCases +) + + + +class TicketCommentSolutionAPITestCases( + TicketCommentBaseAPIInheritedCases, +): + + parameterized_test_data = {} + + kwargs_create_item: dict = {} + + + +class TicketCommentSolutionAPIInheritedCases( + TicketCommentSolutionAPITestCases, +): + + kwargs_create_item: dict = {None} + + model = None + + + +class TicketCommentSolutionAPIPyTest( + TicketCommentSolutionAPITestCases, +): + + pass From d4d99772b9a6342459c78351c38d69c5c5dbd3d5 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 20:42:05 +0930 Subject: [PATCH 073/175] test(core): correct field so its valid for unit TicketCommentBase model ref: #744 #726 --- app/api/tests/unit/test_unit_api_fields.py | 2 ++ .../ticket_comment_base/test_unit_ticket_comment_base_model.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/api/tests/unit/test_unit_api_fields.py b/app/api/tests/unit/test_unit_api_fields.py index 93c329ce..184572a8 100644 --- a/app/api/tests/unit/test_unit_api_fields.py +++ b/app/api/tests/unit/test_unit_api_fields.py @@ -135,6 +135,8 @@ class APIFieldsTestCases: organization = request.cls.organization, ) + request.cls.view_team = view_team + view_team.permissions.set([view_permissions]) diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py index 72dfbc28..95c6c569 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py @@ -40,7 +40,7 @@ class TicketCommentBaseModelTestCases( 'source': TicketBase.TicketSource.HELPDESK, 'user': '', 'is_closed': True, - 'date_closed': '2025-05-08T171000', + 'date_closed': '2025-05-08T17:10Z', } From b6da539fcd831102727fbd94e71da399d400bff7 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 21:02:55 +0930 Subject: [PATCH 074/175] fix(core): Spent slash command is valid for time spent ref: #744 --- app/core/lib/slash_commands/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/core/lib/slash_commands/__init__.py b/app/core/lib/slash_commands/__init__.py index 5bdded1a..79f1eb4e 100644 --- a/app/core/lib/slash_commands/__init__.py +++ b/app/core/lib/slash_commands/__init__.py @@ -67,7 +67,10 @@ class SlashCommands( returned_line = '' - if command == 'spend': + if( + command == 'spend' + or command == 'spent' + ): returned_line = re.sub(self.time_spent, self.command_duration, line) From 0c80b8760603c7472972bd03d720b45658d0c88a Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 21:03:44 +0930 Subject: [PATCH 075/175] test(core): Partial Slash Command re-write ref: #744 #730 --- .../test_slash_command_related.py | 1569 +++++++++++++++++ 1 file changed, 1569 insertions(+) diff --git a/app/core/tests/functional/slash_commands/test_slash_command_related.py b/app/core/tests/functional/slash_commands/test_slash_command_related.py index c8a9996d..b8db51e7 100644 --- a/app/core/tests/functional/slash_commands/test_slash_command_related.py +++ b/app/core/tests/functional/slash_commands/test_slash_command_related.py @@ -1,15 +1,20 @@ import pytest +import re import unittest from django.contrib.auth.models import User from django.test import TestCase from access.models.organization import Organization +from access.models.person import Person from core.models.ticket.ticket import Ticket from core.models.ticket.ticket_comment import TicketComment from core.models.ticket.ticket_linked_items import TicketLinkedItem +from core.models.ticket_comment_base import TicketBase, TicketCommentBase + + from itam.models.device import Device from itam.models.software import Software @@ -1822,3 +1827,1567 @@ def test_slash_command_spend_comment_time_format_comment_correct(test_input, exp assert comment.duration == expected +########################################################################################################### +# +# PyTest re-write +# +########################################################################################################### + + +class SlashCommandsFixtures: + """Common Fixtures + + Fixtures required to setup Ticket and Ticket Comment test cases. + """ + + @pytest.fixture(scope = 'class') + def setup_class(self, request, + organization_one, + django_db_blocker, + ): + + request.cls.organization = organization_one + + with django_db_blocker.unblock(): + + request.cls.ticket_user = User.objects.create_user(username="test_user_for_tickets", password="password") + + + request.cls.entity_user = Person.objects.create( + organization = request.cls.organization, + f_name = 'ip', + l_name = 'funny' + ) + + + request.cls.existing_ticket = Ticket.objects.create( + organization = request.cls.organization, + title = 'an existing ticket', + description = "the ticket body", + ticket_type = Ticket.TicketType.REQUEST, + opened_by = request.cls.ticket_user, + ) + + + + yield + + with django_db_blocker.unblock(): + + request.cls.existing_ticket.delete() + + request.cls.ticket_user.delete() + + request.cls.entity_user.delete() + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_class + ): + pass + + + +class SlashCommandsCommon: + """Common Test Case items + + Required for Ticket Comment and Ticket Slash Commands Test cases. + """ + + single_line_with_command = 'A single line comment COMMAND' + + single_line_command_own_line_lf = 'A single line comment\nCOMMAND' + single_line_command_own_line_crlf = 'A single line comment\r\nCOMMAND' + + single_line_blank_line_command_own_line_lf = 'A single line comment\n\nCOMMAND' + single_line_blank_line_command_own_line_crlf = 'A single line comment\r\n\r\nCOMMAND' + + single_line_blank_line_command_own_line_blank_line_lf = 'A single line comment\n\nCOMMAND\n' + single_line_blank_line_command_own_line_blank_line_crlf = 'A single line comment\r\n\r\nCOMMAND\r\n' + + single_line_command_own_line_blank_line_lf = 'A single line comment\nCOMMAND\n' + single_line_command_own_line_blank_line_crlf = 'A single line comment\r\nCOMMAND\r\n' + + + parameterized_slash_command = { + 'relate_existing_ticket': { + 'relate': True, + 'slash_command': 'relate', + 'command_obj': '#EXISTINGTICKET', + }, + + 'blocks_existing_ticket': { + 'blocks': True, + 'slash_command': 'blocks', + 'command_obj': '#EXISTINGTICKET', + }, + + 'blocked_by_existing_ticket': { + 'blocked_by': True, + 'slash_command': 'blocked_by', + 'command_obj': '#EXISTINGTICKET', + }, + + 'spend_full_no_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h2m3s', + }, + 'spend_full_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h 2m 3s', + }, + 'spend_hour_minute_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h 2m', + }, + 'spend_hour_second_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h 3s', + }, + 'spend_minute_second_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '5m 3s', + }, + 'spend_hour': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h', + }, + 'spend_minute': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1m', + }, + 'spend_second': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '4s', + }, + + 'spent_full_no_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h2m3s', + }, + 'spent_full_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h 2m 3s', + }, + 'spent_hour_minute_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h 2m', + }, + 'spent_hour_second_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h 3s', + }, + 'spent_minute_second_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '5m 3s', + }, + 'spent_hour': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h', + }, + 'spent_minute': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1m', + }, + 'spent_second': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '4s', + }, + + } + + + +class SlashCommandsTicketTestCases( + SlashCommandsCommon +): + """Ticket Test Cases for Slash Commands + + Use these test cases to test tickets for Slash Command functionality. + + Requires a fixture called `Ticket` + """ + pass + + # def test_slash_command_spend_ticket_duration_added(self, + # ticket, + # parameterized, param_key_slash_command, param_name, + # param_slash_command, + # param_command_obj, + # param_spend, + # ): + # """Slash command Check + + # Ensure the `spend` slash command adds the duration to a ticket comment + # within the duration field. + # """ + + # comment_text = self.single_line_command_own_line_blank_line_crlf + + # durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + # hour = durations['hour'] + + # if not hour: + # hour = 0 + + # else: + # hour = str(durations['hour']).replace('h', '') + + # hour = (int(hour) * 60) * 60 + + + # minute = durations['minute'] + + # if not minute: + # minute = 0 + + # else: + # minute = str(durations.get('minute', 0)).replace('m', '') + + # minute = int(minute) * 60 + + + # second = durations['second'] + + # if not second: + # second = 0 + # else: + # second = str(durations['second']).replace('s', '') + + # second = int(second) + + # duration_in_seconds = hour + minute + second + + + # assert 'COMMAND' in comment_text + # # COMMAND must be in ticket comment so it can be constructed + + # command_obj = str(param_command_obj).replace( + # 'EXISTINGTICKET', str(self.existing_ticket.id) + # ) + + # ticket.description = str( + # comment_text.replace( + # 'COMMAND', '/' + param_slash_command + ' ' + command_obj + # ) + # ) + + + # ticket.save() + + # ticket_comment = ticket.ticketcommentbase_set.all() + + # assert len(ticket_comment) == 1 + # # A comment should have been created that contains the date, time and + # # duration of the time spent. + + # ticket_comment = ticket_comment[0] + + + # assert ticket_comment.duration == duration_in_seconds + + + +class SlashCommandsTicketCommentTestCases( + SlashCommandsCommon +): + + # existing_ticket = None + + + def test_slash_command_ticket_comment_single_line_with_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_with_command + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command in ticket_comment.body + and command_obj in ticket_comment.body + ) + + + + def test_slash_command_ticket_comment_single_line_command_own_line_lf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + def test_slash_command_ticket_comment_single_line_command_own_line_crlf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_lf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_crlf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_blank_line_lf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_blank_line_crlf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + + + def test_slash_command_ticket_comment_single_line_command_own_line_blank_line_lf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + + + def test_slash_command_ticket_comment_single_line_command_own_line_blank_line_crlf_command_removed_from_comment(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ( + param_slash_command not in ticket_comment.body + and command_obj not in ticket_comment.body + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + # def test_slash_command_spend_ticket_duration_added(self, + # ticket, + # parameterized, param_key_slash_command, param_name, + # param_slash_command, + # param_command_obj, + # param_spend, + # ): + # """Slash command Check + + # Ensure the `spend` slash command adds the duration to a ticket comment + # within the duration field. + # """ + + # comment_text = self.single_line_command_own_line_blank_line_crlf + + # durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + # hour = durations['hour'] + + # if not hour: + # hour = 0 + + # else: + # hour = str(durations['hour']).replace('h', '') + + # hour = (int(hour) * 60) * 60 + + + # minute = durations['minute'] + + # if not minute: + # minute = 0 + + # else: + # minute = str(durations.get('minute', 0)).replace('m', '') + + # minute = int(minute) * 60 + + + # second = durations['second'] + + # if not second: + # second = 0 + # else: + # second = str(durations['second']).replace('s', '') + + # second = int(second) + + # duration_in_seconds = hour + minute + second + + + # assert 'COMMAND' in comment_text + # # COMMAND must be in ticket comment so it can be constructed + + # command_obj = str(param_command_obj).replace( + # 'EXISTINGTICKET', str(self.existing_ticket.id) + # ) + + # ticket.description = str( + # comment_text.replace( + # 'COMMAND', '/' + param_slash_command + ' ' + command_obj + # ) + # ) + + + # ticket.save() + + # ticket_comment = ticket.ticketcommentbase_set.all() + + # assert len(ticket_comment) == 1 + # # A comment should have been created that contains the date, time and + # # duration of the time spent. + + # ticket_comment = ticket_comment[0] + + + # assert ticket_comment.duration == duration_in_seconds + + + + + def test_slash_command_ticket_comment_single_line_duration_not_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_with_command + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == 0 + + + + def test_slash_command_ticket_comment_single_line_command_own_line_lf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + def test_slash_command_ticket_comment_single_line_command_own_line_crlf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_lf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_crlf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_blank_line_lf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + + + def test_slash_command_ticket_comment_single_line_blank_line_command_own_line_blank_line_crlf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + + + def test_slash_command_ticket_comment_single_line_command_own_line_blank_line_lf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + + + def test_slash_command_ticket_comment_single_line_command_own_line_blank_line_crlf_duration_added(self, + ticket_comment, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket_comment.body = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket_comment.save() + + + assert ticket_comment.duration == duration_in_seconds + + + + + + + + + + + + + + + + + + # def test_slash_command_spend_ticket_comment_duration_added(self, + # ticket_comment, + # parameterized, param_key_slash_command, param_name, + # param_slash_command, + # param_command_obj, + # param_spend, + # ): + # """Slash command Check + + # Ensure the `spend` slash command adds the duration to the tickets + # duration field. + # """ + + # comment_text = self.single_line_command_own_line_blank_line_crlf + + # assert 'COMMAND' in comment_text + # # COMMAND must be in ticket comment so it can be constructed + + # command_obj = str(param_command_obj).replace( + # 'EXISTINGTICKET', str(self.existing_ticket.id) + # ) + + + # durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + # hour = durations['hour'] + + # if not hour: + # hour = 0 + + # else: + # hour = str(durations['hour']).replace('h', '') + + # hour = (int(hour) * 60) * 60 + + + # minute = durations['minute'] + + # if not minute: + # minute = 0 + + # else: + # minute = str(durations.get('minute', 0)).replace('m', '') + + # minute = int(minute) * 60 + + + # second = durations['second'] + + # if not second: + # second = 0 + # else: + # second = str(durations['second']).replace('s', '') + + # second = int(second) + + # duration_in_seconds = hour + minute + second + + # ticket_comment.body = str( + # comment_text.replace( + # 'COMMAND', '/' + param_slash_command + ' ' + command_obj + # ) + # ) + + + # ticket_comment.save() + + + # assert ticket_comment.duration == duration_in_seconds + + + +class SlashCommandsTicketInheritedTestCases( + SlashCommandsFixtures, + SlashCommandsTicketTestCases, +): + + pass + + + +class SlashCommandsTicketCommentInheritedTestCases( + SlashCommandsFixtures, + SlashCommandsTicketCommentTestCases +): + + pass + + + +class SlashCommandsPyTest( + SlashCommandsFixtures, + SlashCommandsTicketTestCases, + SlashCommandsTicketCommentTestCases +): + + + + @pytest.fixture + def ticket(self, request, django_db_blocker): + """ Ticket that requires body + + when using this fixture, set the `description` then call ticket.save() + before use. + """ + + with django_db_blocker.unblock(): + + ticket = TicketBase() + + ticket.organization = request.cls.organization + ticket.title = 'A ticket for slash commands' + ticket.opened_by = request.cls.ticket_user + + ticket = TicketBase.objects.create( + organization = request.cls.organization, + title = 'A ticket for slash commands', + opened_by = request.cls.ticket_user, + ) + + yield ticket + + with django_db_blocker.unblock(): + + ticket.delete() + + + + @pytest.fixture + def ticket_comment(self, request, django_db_blocker, ticket): + """ Ticket Comment that requires body + + when using this fixture, set the `body` then call ticket_comment.save() + before use. + """ + + with django_db_blocker.unblock(): + + ticket.title = 'slash command ticket with comment' + + ticket.save() + + ticket_comment = TicketCommentBase() + + ticket_comment.user = request.cls.entity_user + + ticket_comment.ticket = ticket + + ticket_comment.comment_type = 'comment' + + yield ticket_comment + + ticket_comment.delete() + From 6c3122a3d89e042f7e8d5757b725a3d0f5cc9885 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 21:04:31 +0930 Subject: [PATCH 076/175] test(core): Functional Model test cases (Slash Commands) for TicketBaseModel ref: #744 #723 --- .../test_functional_ticket_base_model.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 app/core/tests/functional/ticket_base/test_functional_ticket_base_model.py diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_model.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_model.py new file mode 100644 index 00000000..0be828db --- /dev/null +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_model.py @@ -0,0 +1,53 @@ +import pytest + +from core.tests.functional.slash_commands.test_slash_command_related import SlashCommandsTicketInheritedTestCases + + + +class TicketBaseModelTestCases( + SlashCommandsTicketInheritedTestCases +): + + + @pytest.fixture + def ticket(self, request, django_db_blocker, model): + """ Ticket that requires body + + when using this fixture, set the `description` then call ticket.save() + before use. + """ + + with django_db_blocker.unblock(): + + ticket = model() + + ticket.organization = request.cls.organization + ticket.title = 'A ticket for slash commands' + ticket.opened_by = request.cls.ticket_user + + # ticket = TicketBase.objects.create( + # organization = request.cls.organization, + # title = 'A ticket for slash commands', + # opened_by = request.cls.ticket_user, + # ) + + yield ticket + + with django_db_blocker.unblock(): + + ticket.delete() + + +class TicketBaseModelInheritedTestCases( + TicketBaseModelTestCases +): + + pass + + + +class TicketBaseModelPyTest( + TicketBaseModelTestCases +): + + pass From a081c6a3715ada187f02b907d3cd23e95ba05189 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 9 May 2025 21:05:54 +0930 Subject: [PATCH 077/175] test(core): Partial Functional Model test cases (Slash Commands) for TicketCommentBase ref: #744 #726 --- .../ticket_comment_base/conftest.py | 14 +++ ...st_functional_ticket_comment_base_model.py | 85 +++++++++++++++++++ .../centurion_erp/development/core/ticket.md | 2 + 3 files changed, 101 insertions(+) create mode 100644 app/core/tests/functional/ticket_comment_base/conftest.py create mode 100644 app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py diff --git a/app/core/tests/functional/ticket_comment_base/conftest.py b/app/core/tests/functional/ticket_comment_base/conftest.py new file mode 100644 index 00000000..daaa25be --- /dev/null +++ b/app/core/tests/functional/ticket_comment_base/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from core.models.ticket_comment_base import TicketCommentBase + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = TicketCommentBase + + yield request.cls.model + + del request.cls.model diff --git a/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py b/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py new file mode 100644 index 00000000..d94ef273 --- /dev/null +++ b/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py @@ -0,0 +1,85 @@ +import pytest + +from core.models.ticket.ticket import Ticket +from core.tests.functional.slash_commands.test_slash_command_related import SlashCommandsTicketCommentInheritedTestCases + + + +class TicketCommentBaseModelTestCases( + SlashCommandsTicketCommentInheritedTestCases +): + + + + @pytest.fixture + def ticket(self, request, django_db_blocker): + """ Ticket that requires body + + when using this fixture, set the `description` then call ticket.save() + before use. + """ + + from core.models.ticket_comment_base import TicketBase + + with django_db_blocker.unblock(): + + ticket = TicketBase() + + ticket.organization = request.cls.organization + ticket.title = 'A ticket for slash commands' + ticket.opened_by = request.cls.ticket_user + + ticket = TicketBase.objects.create( + organization = request.cls.organization, + title = 'A ticket for slash commands', + opened_by = request.cls.ticket_user, + ) + + yield ticket + + with django_db_blocker.unblock(): + + ticket.delete() + + + @pytest.fixture + def ticket_comment(self, request, django_db_blocker, ticket, model): + """ Ticket Comment that requires body + + when using this fixture, set the `body` then call ticket_comment.save() + before use. + """ + + with django_db_blocker.unblock(): + + ticket.title = 'slash command ticket with comment' + + ticket.save() + + ticket_comment = model() + + ticket_comment.user = request.cls.entity_user + + ticket_comment.ticket = ticket + + ticket_comment.comment_type = 'comment' + + yield ticket_comment + + ticket_comment.delete() + + + +class TicketCommentBaseModelInheritedTestCases( + TicketCommentBaseModelTestCases +): + + pass + + + +class TicketCommentBaseModelPyTest( + TicketCommentBaseModelTestCases +): + + pass diff --git a/docs/projects/centurion_erp/development/core/ticket.md b/docs/projects/centurion_erp/development/core/ticket.md index 83b2b4ed..9e1fcf38 100644 --- a/docs/projects/centurion_erp/development/core/ticket.md +++ b/docs/projects/centurion_erp/development/core/ticket.md @@ -40,4 +40,6 @@ As with any other object within Centurion, the addition of a feature requires it - API Permissions `core.tests.functional.ticket_base.test_functional_ticket_base_permission.TicketBasePermissionsAPIInheritedCases` + - Model `app.core.tests.functional.ticket_base.test_functional_ticket_base_model.TicketBaseModelInheritedTestCases` _(if inheriting from `TicketBase`)_ Test cases for sub-models + The above listed test cases cover **all** tests for objects that are inherited from the base class. To complete the tests, you will need to add test cases for the differences your model introduces. From 85c6ed84832cac4408a22ae112ee58b76543df64 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 13:26:22 +0930 Subject: [PATCH 078/175] test(core): Add ability to unit api field rendering test case for second api request if required ref: #744 #726 --- app/api/tests/unit/test_unit_api_fields.py | 58 ++++++++++++++- ...est_unit_ticket_comment_base_api_fields.py | 73 +++++++++++++------ 2 files changed, 105 insertions(+), 26 deletions(-) diff --git a/app/api/tests/unit/test_unit_api_fields.py b/app/api/tests/unit/test_unit_api_fields.py index 184572a8..c7745164 100644 --- a/app/api/tests/unit/test_unit_api_fields.py +++ b/app/api/tests/unit/test_unit_api_fields.py @@ -14,6 +14,25 @@ from app.tests.common import DoesNotExist class APIFieldsTestCases: + """ API field Rendering Test Suite + + This test suite tests the rendering of API fieilds. + + ## Additional Items + + You may find a scenario where you are unable to have all fileds available + within a single request. to overcome this this test suite has the features + available wherein you can prepare an additional item for an additional + check. the following is required before the API request is made + (setup_post fixture): + + - additional item created and stored in attribute `self.item_two` + - additional url as a string and stored in attribute `self.url_two` + + Once you have these two objects, an additional check will be done and each + test will check both API requests. if the field is found in either api + request the test will pass + """ @property def parameterized_test_data(self) -> dict: @@ -178,10 +197,25 @@ class APIFieldsTestCases: request.cls.api_data = response.data + item_two = getattr(request.cls, 'url_two', None) + + if item_two: + + response_two = client.get(request.cls.url_two) + + request.cls.api_data_two = response_two.data + + else: + + request.cls.api_data_two = {} + + yield del request.cls.url_view_kwargs['pk'] + del request.cls.api_data_two + @@ -203,13 +237,21 @@ class APIFieldsTestCases: api_data = recursearray(self.api_data, param_value) + api_data_two = recursearray(self.api_data_two, param_value) + if param_expected is DoesNotExist: - assert api_data['key'] not in api_data['obj'] + assert( + api_data['key'] not in api_data['obj'] + and api_data_two['key'] not in api_data_two['obj'] + ) else: - assert api_data['key'] in api_data['obj'] + assert( + api_data['key'] in api_data['obj'] + or api_data_two['key'] in api_data_two['obj'] + ) @@ -221,13 +263,21 @@ class APIFieldsTestCases: api_data = recursearray(self.api_data, param_value) + api_data_two = recursearray(self.api_data_two, param_value) + if param_expected is DoesNotExist: - assert api_data['key'] not in api_data['obj'] + assert( + api_data['key'] not in api_data['obj'] + and api_data_two['key'] not in api_data_two['obj'] + ) else: - assert type( api_data['value'] ) is param_expected + assert( + type( api_data['value'] ) is param_expected + or type( api_data_two.get('value', 'is empty') ) is param_expected + ) diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py index 7bf2c144..2c61810d 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py @@ -1,6 +1,7 @@ import pytest from django.contrib.auth.models import ContentType, Permission, User +from django.shortcuts import reverse from rest_framework.relations import Hyperlink @@ -65,27 +66,24 @@ class TicketCommentBaseAPITestCases( organization = request.cls.organization, ) + request.cls.comment_user = comment_user + valid_data = request.cls.kwargs_create_item.copy() - valid_data['body'] = 'the parent comment' - valid_data['user'] = comment_user - valid_data['ticket'] = ticket - valid_data['comment_type'] = TicketCommentBase._meta.sub_model_type - + valid_data['body'] = 'the template comment' + del valid_data['external_ref'] del valid_data['external_system'] del valid_data['category'] - del valid_data['parent'] del valid_data['template'] + del valid_data['parent'] - parent_comment = TicketCommentBase.objects.create( - **valid_data - ) + valid_data['comment_type'] = TicketCommentBase._meta.sub_model_type + valid_data['ticket'] = ticket + valid_data['user'] = request.cls.comment_user - valid_data['body'] = 'the template comment' - template_comment = TicketCommentBase.objects.create( **valid_data ) @@ -95,25 +93,22 @@ class TicketCommentBaseAPITestCases( 'category': category, 'ticket': ticket, 'user': comment_user, - 'parent': parent_comment, + 'parent': None, 'template': template_comment, 'comment_type': model._meta.sub_model_type }) - yield - with django_db_blocker.unblock(): - parent_comment.delete() - template_comment.delete() category.delete() + del request.cls.comment_user for comment in ticket.ticketcommentbase_set.all(): @@ -128,7 +123,7 @@ class TicketCommentBaseAPITestCases( @pytest.fixture( scope = 'class') - def post_model(self, request, model ): + def post_model(self, request, model, django_db_blocker ): request.cls.url_view_kwargs.update({ 'ticket_id': request.cls.item.ticket.id @@ -143,14 +138,48 @@ class TicketCommentBaseAPITestCases( 'ticket_comment_model': model._meta.sub_model_type }) - if self.item.parent: - request.cls.url_ns_name = '_api_v2_ticket_comment_base_sub_thread' + valid_data = request.cls.kwargs_create_item.copy() + valid_data['body'] = 'the child comment' - request.cls.url_view_kwargs.update({ - 'parent_id': self.item.parent.id - }) + valid_data['comment_type'] = TicketCommentBase._meta.sub_model_type + valid_data['parent'] = request.cls.item + valid_data['ticket'] = request.cls.item.ticket + valid_data['user'] = request.cls.comment_user + del valid_data['external_ref'] + del valid_data['external_system'] + del valid_data['category'] + del valid_data['template'] + + with django_db_blocker.unblock(): + + request.cls.item_two = TicketCommentBase.objects.create( + **valid_data + ) + + url_ns_name = '_api_v2_ticket_comment_base_sub_thread' + + request.cls.url_two = reverse( + 'v2:' + url_ns_name + '-detail', + kwargs = { + **request.cls.url_view_kwargs, + 'pk': request.cls.item_two.id, + 'parent_id': request.cls.item.id, + 'ticket_comment_model': model._meta.sub_model_type + } + ) + + + yield + + with django_db_blocker.unblock(): + + request.cls.item_two.delete(keep_parents = False) + + del request.cls.item_two + + del request.cls.url_two From 03b752759f0a695c301d8b26aec537b971b2b6f1 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 14:34:39 +0930 Subject: [PATCH 079/175] test(core): Skip Related slash command checks until migrating tickets to new model ref: #744 #746 --- .../test_slash_command_related.py | 208 ++++++++++-------- 1 file changed, 111 insertions(+), 97 deletions(-) diff --git a/app/core/tests/functional/slash_commands/test_slash_command_related.py b/app/core/tests/functional/slash_commands/test_slash_command_related.py index b8db51e7..3e597d88 100644 --- a/app/core/tests/functional/slash_commands/test_slash_command_related.py +++ b/app/core/tests/functional/slash_commands/test_slash_command_related.py @@ -1910,108 +1910,122 @@ class SlashCommandsCommon: single_line_command_own_line_blank_line_crlf = 'A single line comment\r\nCOMMAND\r\n' - parameterized_slash_command = { - 'relate_existing_ticket': { - 'relate': True, - 'slash_command': 'relate', - 'command_obj': '#EXISTINGTICKET', - }, + @property + def parameterized_slash_command(self): + + return { + ############################################################################################### + # + # Sof Skipped due to ticket model re-write. + # model core.models.ticket.ticket.RelatedTickets still uses the old ticket model + # + ############################################################################################### + # 'relate_existing_ticket': { + # 'relate': True, + # 'slash_command': 'relate', + # 'command_obj': '#EXISTINGTICKET', + # }, - 'blocks_existing_ticket': { - 'blocks': True, - 'slash_command': 'blocks', - 'command_obj': '#EXISTINGTICKET', - }, + # 'blocks_existing_ticket': { + # 'blocks': True, + # 'slash_command': 'blocks', + # 'command_obj': '#EXISTINGTICKET', + # }, - 'blocked_by_existing_ticket': { - 'blocked_by': True, - 'slash_command': 'blocked_by', - 'command_obj': '#EXISTINGTICKET', - }, + # 'blocked_by_existing_ticket': { + # 'blocked_by': True, + # 'slash_command': 'blocked_by', + # 'command_obj': '#EXISTINGTICKET', + # }, + ############################################################################################### + # + # Eof Skipped due to ticket model re-write. + # + ############################################################################################### - 'spend_full_no_spaces': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '1h2m3s', - }, - 'spend_full_spaces': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '1h 2m 3s', - }, - 'spend_hour_minute_spaces': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '1h 2m', - }, - 'spend_hour_second_spaces': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '1h 3s', - }, - 'spend_minute_second_spaces': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '5m 3s', - }, - 'spend_hour': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '1h', - }, - 'spend_minute': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '1m', - }, - 'spend_second': { - 'spend': True, - 'slash_command': 'spend', - 'command_obj': '4s', - }, + 'spend_full_no_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h2m3s', + }, + 'spend_full_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h 2m 3s', + }, + 'spend_hour_minute_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h 2m', + }, + 'spend_hour_second_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h 3s', + }, + 'spend_minute_second_spaces': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '5m 3s', + }, + 'spend_hour': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1h', + }, + 'spend_minute': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '1m', + }, + 'spend_second': { + 'spend': True, + 'slash_command': 'spend', + 'command_obj': '4s', + }, - 'spent_full_no_spaces': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '1h2m3s', - }, - 'spent_full_spaces': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '1h 2m 3s', - }, - 'spent_hour_minute_spaces': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '1h 2m', - }, - 'spent_hour_second_spaces': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '1h 3s', - }, - 'spent_minute_second_spaces': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '5m 3s', - }, - 'spent_hour': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '1h', - }, - 'spent_minute': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '1m', - }, - 'spent_second': { - 'spend': True, - 'slash_command': 'spent', - 'command_obj': '4s', - }, + 'spent_full_no_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h2m3s', + }, + 'spent_full_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h 2m 3s', + }, + 'spent_hour_minute_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h 2m', + }, + 'spent_hour_second_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h 3s', + }, + 'spent_minute_second_spaces': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '5m 3s', + }, + 'spent_hour': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1h', + }, + 'spent_minute': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '1m', + }, + 'spent_second': { + 'spend': True, + 'slash_command': 'spent', + 'command_obj': '4s', + }, - } + } From 55f58bb689ef644f42443308c97bd74a3e4fbe86 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 14:36:16 +0930 Subject: [PATCH 080/175] test(core): Unit ViewSet Test Suite for TicketCommentBase ref: #744 #726 --- .../test_unit_ticket_comment_base_viewset.py | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py new file mode 100644 index 00000000..3ee30e00 --- /dev/null +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py @@ -0,0 +1,121 @@ +from django.contrib.auth.models import User +from django.test import Client, TestCase + +from rest_framework.reverse import reverse + +from api.tests.unit.test_unit_common_viewset import SubModelViewSetInheritedCases + +from core.viewsets.ticket_comment import ( + NoDocsViewSet, + TicketBase, + TicketCommentBase, + ViewSet +) + + +class TicketCommentBaseViewsetTestCases( + SubModelViewSetInheritedCases, +): + + model = None + + viewset = ViewSet + + base_model = TicketCommentBase + + route_name = None + + + @classmethod + def setUpTestData(self): + + + self.viewset = ViewSet + + + if self.model is None: + + self.model = TicketCommentBase + + + + super().setUpTestData() + + self.ticket = TicketBase.objects.create( + organization = self.organization, + title = 'ticket comment test', + opened_by = self.view_user, + ) + + self.kwargs = { + 'ticket_id': self.ticket.id + } + + if self.model is not TicketCommentBase: + + self.kwargs = { + **self.kwargs, + 'ticket_comment_model': self.model._meta.sub_model_type + } + + self.viewset.kwargs = self.kwargs + + + client = Client() + + url = reverse( + self.route_name + '-list', + kwargs = self.kwargs + ) + + client.force_login(self.view_user) + + self.http_options_response_list = client.options(url) + + + @classmethod + def tearDownClass(cls): + + cls.ticket.delete() + + super().tearDownClass() + + + + def test_view_attr_value_model_kwarg(self): + """Attribute Test + + Attribute `model_kwarg` must be equal to model._meta.sub_model_type + """ + + view_set = self.viewset() + + assert view_set.model_kwarg == 'ticket_comment_model' + + + +class TicketCommentBaseViewsetInheritedCases( + TicketCommentBaseViewsetTestCases, +): + """Test Suite for Sub-Models of TicketCommentBase + + Use this Test suit if your sub-model inherits directly from TicketCommentBase. + """ + + model: str = None + """name of the model to test""" + + route_name = 'v2:_api_v2_ticket_comment_base_sub' + + + +class TicketCommentBaseViewsetTest( + TicketCommentBaseViewsetTestCases, + TestCase, +): + + kwargs = {} + + route_name = 'v2:_api_v2_ticket_comment_base' + + viewset = NoDocsViewSet From f71e304731d599ed42c8ea6a80777730b060e32f Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 15:24:50 +0930 Subject: [PATCH 081/175] test(core): Unit ViewSet Test Suite for TicketCommentSolution ref: #744 #728 --- ...st_unit_ticket_comment_solution_viewset.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_viewset.py diff --git a/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_viewset.py b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_viewset.py new file mode 100644 index 00000000..fc8cdaf8 --- /dev/null +++ b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_viewset.py @@ -0,0 +1,39 @@ +from django.test import TestCase + +from core.models.ticket_comment_solution import TicketCommentSolution +from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_viewset import TicketCommentBaseViewsetInheritedCases + + + +class TicketCommentSolutionViewsetTestCases( + TicketCommentBaseViewsetInheritedCases, +): + + + @classmethod + def setUpTestData(self): + + self.model = TicketCommentSolution + + super().setUpTestData() + + + +class TicketCommentSolutionViewsetInheritedCases( + TicketCommentSolutionViewsetTestCases, +): + """Test Suite for Sub-Models of TicketBase + + Use this Test suit if your sub-model inherits directly from TicketCommentSolution. + """ + + model: str = None + """name of the model to test""" + + + +class TicketCommentSolutionViewsetTest( + TicketCommentSolutionViewsetTestCases, + TestCase, +): + pass From e366220e8bd13d2400ebc6a09c565b9d2d7863d2 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 16:42:05 +0930 Subject: [PATCH 082/175] fix(core): ensure slash command is called on ticket description ref: #744 #723 --- app/core/models/ticket_base.py | 13 ++++++ .../test_unit_ticket_base_model.py | 44 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/app/core/models/ticket_base.py b/app/core/models/ticket_base.py index 2952879f..70131ef0 100644 --- a/app/core/models/ticket_base.py +++ b/app/core/models/ticket_base.py @@ -819,5 +819,18 @@ class TicketBase( self.date_closed = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() + + if( + self.description != '' + and self.description is not None + ): + + description = self.slash_command(self.description) + + if description != self.description: + + self.description = description + + super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py index 2e4f7b9c..fc1133fb 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py @@ -893,6 +893,26 @@ class TicketBaseModelTestCases( assert spy.assert_called_once + def test_function_save_called_slash_command(self, model, mocker, ticket): + """Function Check + + Ensure function `TicketCommentBase.clean` is called + """ + + spy = mocker.spy(self.model, 'slash_command') + + valid_data = self.kwargs_create_item.copy() + + valid_data['title'] = 'was save called' + + del valid_data['external_system'] + + item = model.objects.create( + **valid_data + ) + + spy.assert_called_with(item, valid_data['description']) + class TicketBaseModelInheritedCases( @@ -945,3 +965,27 @@ class TicketBaseModelPyTest( """ assert type(self.model().get_related_model()) is type(None) + + + def test_function_save_called_slash_command(self, model, mocker, ticket): + """Function Check + + This test case is a duplicate of a test with the same name. This + test is required so that the base class `save()` function can be tested. + + Ensure function `TicketCommentBase.clean` is called + """ + + spy = mocker.spy(self.model, 'slash_command') + + valid_data = self.kwargs_create_item.copy() + + valid_data['title'] = 'was save called' + + del valid_data['external_system'] + + item = model.objects.create( + **valid_data + ) + + spy.assert_called_with(item, valid_data['description']) From a6e0f4e7289357986185b9256c24c82936f5f2dd Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 16:46:13 +0930 Subject: [PATCH 083/175] test(core): ensure slash command is called on ticket comment ref: #744 #726 --- .../test_unit_ticket_comment_base_model.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py index 95c6c569..0457c6c1 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py @@ -186,6 +186,7 @@ class TicketCommentBaseModelTestCases( ticket = TicketBase.objects.create( organization = request.cls.organization, title = 'tester comment ticket', + description = 'aa', opened_by = request.cls.view_user, ) @@ -480,6 +481,28 @@ class TicketCommentBaseModelTestCases( assert spy.assert_called_once + def test_function_save_called_slash_command(self, model, mocker, ticket): + """Function Check + + Ensure function `TicketCommentBase.clean` is called + """ + + spy = mocker.spy(self.model, 'slash_command') + + valid_data = self.kwargs_create_item.copy() + + valid_data['ticket'] = ticket + + del valid_data['external_system'] + del valid_data['external_ref'] + + item = model.objects.create( + **valid_data + ) + + spy.assert_called_with(item, valid_data['body']) + + class TicketCommentBaseModelInheritedCases( TicketCommentBaseModelTestCases, @@ -526,3 +549,28 @@ class TicketCommentBaseModelPyTest( ) assert err.value.get_codes()['date_closed'] == 'ticket_closed_no_date' + + + def test_function_save_called_slash_command(self, model, mocker, ticket): + """Function Check + + This test case is a duplicate of a test with the same name. This + test is required so that the base class `save()` function can be tested. + + Ensure function `TicketCommentBase.clean` is called + """ + + spy = mocker.spy(self.model, 'slash_command') + + valid_data = self.kwargs_create_item.copy() + + valid_data['ticket'] = ticket + + del valid_data['external_system'] + del valid_data['external_ref'] + + item = model.objects.create( + **valid_data + ) + + spy.assert_called_with(item, valid_data['body']) From b22baefa5a789877e2912231cef1ff47d64e0ade Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 17:24:07 +0930 Subject: [PATCH 084/175] test(core): ensure ticket is un-solved for ticketcomment unit api render fields check ref: #744 #726 --- .gitignore | 2 ++ .../test_unit_ticket_comment_base_api_fields.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1b54c405..f262b358 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ __pycache__ **.sqlite3 **.sqlite **.coverage +.coverage* artifacts/ **.tmp.* volumes/ @@ -19,3 +20,4 @@ package.json feature_flags.json coverage_*.json *-coverage.xml + diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py index 2c61810d..ef5321aa 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py @@ -154,7 +154,14 @@ class TicketCommentBaseAPITestCases( with django_db_blocker.unblock(): - request.cls.item_two = TicketCommentBase.objects.create( + request.cls.item.ticket.is_closed = False + request.cls.item.ticket.date_closed = None + request.cls.item.ticket.is_solved = False + request.cls.item.ticket.date_solved = None + request.cls.item.ticket.status = TicketBase.TicketStatus.NEW + request.cls.item.ticket.save() + + request.cls.item_two = model.objects.create( **valid_data ) From 24b6bcfa47194e6954bfe1847534493a51137140 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 21:03:58 +0930 Subject: [PATCH 085/175] feat(base): Enable user to customize log file location ref: #744 #436 #752 --- .gitignore | 2 +- Release-Notes.md | 19 ++++++ app/app/settings.py | 108 ++++++++++++++++++++++++++++++++++ includes/etc/itsm/settings.py | 9 +++ 4 files changed, 137 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f262b358..201ad60b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ package.json feature_flags.json coverage_*.json *-coverage.xml - +log/ diff --git a/Release-Notes.md b/Release-Notes.md index 51589fba..88c08fd9 100644 --- a/Release-Notes.md +++ b/Release-Notes.md @@ -1,3 +1,22 @@ +## Version 1.17.0 + +- Added setting for log files. + + Enables user to specify a default path for centurion's logging. Add the following to your settings file `/etc/itsm/settings.py` + + ``` py + LOG_FILES = { + "centurion": "/var/log/centurion.log", # Normal Centurion Operations + "weblog": "/var/log/weblog.log", # All web requests made to Centurion + "rest_api": "/var/log/rest_api.log", # Rest API + "catch_all":"/var/log/catch-all.log" # A catch all log. Note: does not log anything that has already been logged. + } + + ``` + + With this new setting, the previous setting `LOGGING` will no longer function. + + ## Version 1.16.0 - Employees model added behind feature flag `2025-00002` and will remain behind this flag until production ready. diff --git a/app/app/settings.py b/app/app/settings.py index 43a866ce..6da519f3 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -80,6 +80,95 @@ FEATURE_FLAG_OVERRIDES = None # Feature Flags to override fetched feature flags # PROMETHEUS_METRICS_EXPORT_PORT = 8010 # PROMETHEUS_METRICS_EXPORT_ADDRESS = '' + +LOG_FILES = { # defaults for devopment. docker includes settings has correct locations + "centurion": "../log/centurion.log", + "weblog": "../log/weblog.log", + "rest_api": "../log/rest_api.log", + "catch_all":"../log/catch-all.log" +} + +CENTURION_LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "console": { + "format": "{asctime} {levelname} {message}", + "style": "{", + }, + "verbose": { + "format": "{asctime} {levelname} {name} {module} {process:d} {thread:d} {message}", + "style": "{", + }, + "simple": { + "format": "{levelname} {message}", + "style": "{", + }, + "web_log": { + "format": "{asctime} {levelname} {name} {module} {process:d} {thread:d} {message}", + "style": "{", + }, + }, + "handlers": { + 'console': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'console', + }, + "file_centurion": { + "level": "INFO", + "class": "logging.FileHandler", + "filename": "centurion.log", + 'formatter': 'verbose', + }, + "file_weblog": { + "level": "INFO", + "class": "logging.FileHandler", + "filename": "weblog.log", + 'formatter': 'web_log', + }, + "file_rest_api": { + "level": "INFO", + "class": "logging.FileHandler", + "filename": "rest_api.log", + 'formatter': 'verbose', + }, + "file_catch_all": { + "level": "INFO", + "class": "logging.FileHandler", + "filename": "catch-all.log", + 'formatter': 'verbose', + } + }, + "loggers": { + "centurion": { + "handlers": ['console', 'file_centurion'], + "level": "INFO", + "propagate": False, + }, + "django.server": { + "handlers": ["file_weblog", 'console'], + "level": "INFO", + "propagate": False, + }, + "django": { + "handlers": ['console', 'file_catch_all'], + "level": "INFO", + "propagate": False, + }, + 'rest_framework': { + 'handlers': ['file_rest_api', 'console'], + 'level': 'INFO', + 'propagate': False, + }, + '': { + 'handlers': ['file_catch_all'], + 'level': 'INFO', + 'propagate': True, + }, + }, + } + METRICS_ENABLED = False # Enable Metrics METRICS_EXPORT_PORT = 8080 # Port to serve metrics on METRICS_MULTIPROC_DIR = '/tmp/prometheus' # path the metrics from multiple-process' save to @@ -393,7 +482,22 @@ CSRF_TRUSTED_ORIGINS = [ *TRUSTED_ORIGINS ] + +# Add the user specified log files +CENTURION_LOGGING['handlers']['file_centurion']['filename'] = LOG_FILES['centurion'] +CENTURION_LOGGING['handlers']['file_weblog']['filename'] = LOG_FILES['weblog'] +CENTURION_LOGGING['handlers']['file_rest_api']['filename'] = LOG_FILES['rest_api'] +CENTURION_LOGGING['handlers']['file_catch_all']['filename'] = LOG_FILES['catch_all'] + + +if str(CENTURION_LOGGING['handlers']['file_centurion']['filename']).startswith('../log'): + + if not os.path.exists('../log'): # Create log dir + + os.makedirs('../log') + if DEBUG: + INSTALLED_APPS += [ 'debug_toolbar', ] @@ -407,6 +511,10 @@ if DEBUG: ] +# Setup Logging +LOGGING = CENTURION_LOGGING + + if METRICS_ENABLED: INSTALLED_APPS += [ 'django_prometheus', ] diff --git a/includes/etc/itsm/settings.py b/includes/etc/itsm/settings.py index e209b5e9..51abbb59 100644 --- a/includes/etc/itsm/settings.py +++ b/includes/etc/itsm/settings.py @@ -33,6 +33,15 @@ FEATURE_FLAGGING_ENABLED = True # Turn Feature Flagging on/off FEATURE_FLAG_OVERRIDES = [] # Feature Flag Overrides. Takes preceedence over downloaded feature flags. + +LOG_FILES = { # Location where log files will be created + "centurion": "/var/log/centurion.log", + "weblog": "/var/log/weblog.log", + "rest_api": "/var/log/rest_api.log", + "catch_all":"/var/log/catch-all.log" +} + + SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_SSL_REDIRECT = True From 1bff76c6371ae208a27ff25dca79d577fc031c8f Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 23:38:58 +0930 Subject: [PATCH 086/175] feat(access): Add Logging function to Tenancy model ref: #744 #436 #752 --- app/access/models/tenancy.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/access/models/tenancy.py b/app/access/models/tenancy.py index ddec70ac..427d9a8e 100644 --- a/app/access/models/tenancy.py +++ b/app/access/models/tenancy.py @@ -1,4 +1,4 @@ -# from django.conf import settings +import logging from django.db import models # from django.contrib.auth.models import User, Group @@ -193,6 +193,16 @@ class TenancyObject(SaveHistory): only be used when there is model inheritence. """ + _log: logging.Logger = None + + def get_log(self): + + if self._log is None: + + self._log = logging.getLogger('centurion.' + self._meta.app_label) + + return self._log + page_layout: list = None note_basename: str = None From 40b51f1a7790ea5a370399737af5fab12cb68d38 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 23:39:22 +0930 Subject: [PATCH 087/175] feat(api): Add Logging function to Common ViewSet ref: #744 #436 #752 --- app/api/viewsets/common.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index 3e745285..27779c49 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -1,4 +1,6 @@ import importlib +import logging + from django.utils.safestring import mark_safe from rest_framework import viewsets, pagination @@ -242,6 +244,8 @@ class Retrieve( status = 501 ) + self.get_log().exception(e) + else: response = Response( @@ -315,6 +319,8 @@ class Update( status = 501 ) + self.get_log().exception(e) + else: response = Response( @@ -382,6 +388,8 @@ class Update( status = 501 ) + self.get_log().exception(e) + else: response = Response( @@ -431,6 +439,16 @@ class CommonViewSet( _Optional_, if specified will be add to list view metadata """ + _log: logging.Logger = None + + def get_log(self): + + if self._log is None: + + self._log = logging.getLogger('centurion.' + self.model._meta.app_label) + + return self._log + metadata_class = ReactUIMetadata """ Metadata Class From 70c835eb93b4cbed3812a0fd4866af69323c120f Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 23:40:41 +0930 Subject: [PATCH 088/175] test(core): Partial functional Model Test Suite covering some slash commande for TicketCommentSolution ref: #744 #728 --- ...st_functional_ticket_comment_base_model.py | 2 +- .../ticket_comment_solution/conftest.py | 14 ++++++++++ ...unctional_ticket_comment_solution_model.py | 28 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 app/core/tests/functional/ticket_comment_solution/conftest.py create mode 100644 app/core/tests/functional/ticket_comment_solution/test_functional_ticket_comment_solution_model.py diff --git a/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py b/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py index d94ef273..d48e5244 100644 --- a/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py +++ b/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py @@ -62,7 +62,7 @@ class TicketCommentBaseModelTestCases( ticket_comment.ticket = ticket - ticket_comment.comment_type = 'comment' + ticket_comment.comment_type = model._meta.sub_model_type yield ticket_comment diff --git a/app/core/tests/functional/ticket_comment_solution/conftest.py b/app/core/tests/functional/ticket_comment_solution/conftest.py new file mode 100644 index 00000000..0b91e73a --- /dev/null +++ b/app/core/tests/functional/ticket_comment_solution/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from core.models.ticket_comment_solution import TicketCommentSolution + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = TicketCommentSolution + + yield request.cls.model + + del request.cls.model diff --git a/app/core/tests/functional/ticket_comment_solution/test_functional_ticket_comment_solution_model.py b/app/core/tests/functional/ticket_comment_solution/test_functional_ticket_comment_solution_model.py new file mode 100644 index 00000000..6fb6d20a --- /dev/null +++ b/app/core/tests/functional/ticket_comment_solution/test_functional_ticket_comment_solution_model.py @@ -0,0 +1,28 @@ +from core.tests.functional.ticket_comment_base.test_functional_ticket_comment_base_model import TicketCommentBaseModelInheritedTestCases + + +class TicketCommentSolutionModelTestCases( + TicketCommentBaseModelInheritedTestCases +): + pass + + + # check closes ticket + + # check ticket status changes to solved + + + +class TicketCommentSolutionModelInheritedTestCases( + TicketCommentSolutionModelTestCases +): + + pass + + + +class TicketCommentSolutionModelPyTest( + TicketCommentSolutionModelTestCases +): + + pass From cb49d0fbf754fdcf523cebee529eae44957a54c6 Mon Sep 17 00:00:00 2001 From: Jon Date: Sat, 10 May 2025 23:42:40 +0930 Subject: [PATCH 089/175] feat(core): When processing slash command duration, cater for new ticket models ref: #744 #728 #726 #746 #747 --- app/core/lib/slash_commands/duration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/core/lib/slash_commands/duration.py b/app/core/lib/slash_commands/duration.py index adf8e20c..bef64060 100644 --- a/app/core/lib/slash_commands/duration.py +++ b/app/core/lib/slash_commands/duration.py @@ -82,7 +82,10 @@ For this command to process the following conditions must be met: user = self.opened_by, ) - elif str(self._meta.verbose_name).lower().replace(' ', '_') == 'ticket_comment': + elif( + str(self._meta.verbose_name).lower().replace(' ', '_') == 'ticket_comment' + or str(self.__class__.__name__).lower().startswith('ticketcomment') + ): self.duration = duration From d8ef918a67241e867b13795973ebd0e467123ffd Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 11 May 2025 01:41:30 +0930 Subject: [PATCH 090/175] feat(python): Upgrade DRF 3.15.2 -> 3.16.0 ref: #744 --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 53804067..2547c463 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,11 +5,11 @@ django-cors-headers==4.4.0 django-debug-toolbar==5.1.0 social-auth-app-django==5.4.1 -djangorestframework==3.15.2 -djangorestframework-jsonapi==7.0.2 +djangorestframework==3.16.0 +djangorestframework-jsonapi==7.1.0 # DRF -pyyaml>=6.0.1 +pyyaml>=6.0.2 django-filter==24.2 # OpenAPI Schema From 4d7510ad3a79e1d902cfdd8bdb47f74326627c2e Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 11 May 2025 01:42:08 +0930 Subject: [PATCH 091/175] feat(python): Upgrade DRF Spectacular 0.27.2 -> 0.28.0 ref: #744 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2547c463..1710772f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,8 +16,8 @@ django-filter==24.2 uritemplate==4.1.1 coreapi==2.3.3 -drf-spectacular==0.27.2 -drf-spectacular[sidecar]==0.27.2 +drf-spectacular==0.28.0 +drf-spectacular[sidecar]==0.28.0 django_split_settings==1.3.1 From e29a7ec0e2c4bc445e5dff146571bc19f64efdf6 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 02:49:17 +0930 Subject: [PATCH 092/175] test(core): TicketBase Remaining Serializer Chacks ref: #753 #723 --- .vscode/settings.json | 1 + ...024_alter_ticketbase_opened_by_and_more.py | 47 ++ app/core/models/ticket_base.py | 109 +++-- app/core/serializers/ticket.py | 35 +- .../test_functional_ticket_base_serializer.py | 450 ++++++++++++++---- .../test_unit_ticket_base_api_fields.py | 3 + .../test_unit_ticket_base_model.py | 4 +- .../centurion_erp/user/core/tickets.md | 23 +- 8 files changed, 518 insertions(+), 154 deletions(-) create mode 100644 app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py diff --git a/.vscode/settings.json b/.vscode/settings.json index e022509a..5497b350 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,4 +23,5 @@ "yellow": 60, "green": 90 }, + "telemetry.feedback.enabled": false, } \ No newline at end of file diff --git a/app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py b/app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py new file mode 100644 index 00000000..18bffbfe --- /dev/null +++ b/app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py @@ -0,0 +1,47 @@ +# Generated by Django 5.1.9 on 2025-05-11 16:49 + +import core.models.ticket_comment_base +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0023_alter_ticketlinkeditem_item_type'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='ticketbase', + name='opened_by', + field=models.ForeignKey(blank=True, help_text='Who is the ticket for', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ticket_opened', to=settings.AUTH_USER_MODEL, verbose_name='Opened By'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='category', + field=models.ForeignKey(blank=True, help_text='Category of the comment', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.ticketcommentcategory', verbose_name='Category'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='comment_type', + field=models.CharField(choices=core.models.ticket_comment_base.TicketCommentBase.get_comment_type_choices, help_text='Type this comment is. derived from Meta.verbose_name', max_length=30, validators=[core.models.ticket_comment_base.TicketCommentBase.field_validation_not_empty], verbose_name='Type'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='external_ref', + field=models.IntegerField(blank=True, help_text='External System reference', null=True, verbose_name='Reference Number'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='external_system', + field=models.IntegerField(blank=True, choices=[(1, 'Github'), (2, 'Gitlab'), (9999, 'Custom #1 (Imported)'), (9998, 'Custom #2 (Imported)'), (9997, 'Custom #3 (Imported)'), (9996, 'Custom #4 (Imported)'), (9995, 'Custom #5 (Imported)'), (9994, 'Custom #6 (Imported)'), (9993, 'Custom #7 (Imported)'), (9992, 'Custom #8 (Imported)'), (9991, 'Custom #9 (Imported)')], help_text='External system this item derives', null=True, verbose_name='External System'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='parent', + field=models.ForeignKey(blank=True, help_text='Parent ID for creating discussion threads', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.ticketcommentbase', verbose_name='Parent Comment'), + ), + ] diff --git a/app/core/models/ticket_base.py b/app/core/models/ticket_base.py index 70131ef0..517b9992 100644 --- a/app/core/models/ticket_base.py +++ b/app/core/models/ticket_base.py @@ -434,9 +434,9 @@ class TicketBase( opened_by = models.ForeignKey( User, - blank = False, + blank = True, help_text = 'Who is the ticket for', - null = False, + null = True, on_delete = models.PROTECT, related_name = 'ticket_opened', verbose_name = 'Opened By', @@ -567,6 +567,16 @@ class TicketBase( there are unresolved ticket comments. """ + if self.opened_by is None: + + raise centurion_exception.ValidationError( + detail = { + 'opened_by': 'This field is required.' + }, + code = 'required' + ) + + if self.milestone: if self.milestone.project != self.project: @@ -578,17 +588,80 @@ class TicketBase( code = 'milestone_different_project' ) + + if( + ( + self.status == self.TicketStatus.SOLVED + and self.get_can_resolve( raise_exceptions = False ) + ) + or self.status == self.TicketStatus.INVALID + ): + + self.is_solved = True + + + elif( # Re-Open Ticket + ( self.is_solved or self.is_closed ) + and self.status != self.TicketStatus.CLOSED + and self.status != self.TicketStatus.INVALID + and self.status != self.TicketStatus.SOLVED + ): + + if self.is_closed: + + self.is_closed = False + + if self.is_solved: + + self.is_solved = False + + + elif not self.is_closed and self.status == self.TicketStatus.CLOSED: # Close Ticket + + self.is_solved = True + self.is_closed = True + + if self.is_solved: self.get_can_resolve( raise_exceptions = True ) - if self.is_closed: self.get_can_close( raise_exceptions = True ) + related_model = self.get_related_model() + + if related_model is None: + + related_model = self + + if self.ticket_type != str(related_model._meta.sub_model_type).lower().replace(' ', '_'): + + self.ticket_type = str(related_model._meta.sub_model_type).lower().replace(' ', '_') + + + if self.date_solved is None and self.is_solved: + + self.date_solved = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() + + elif self.date_solved is not None and not self.is_solved: + + self.date_solved = None + + + if self.date_closed is None and self.is_closed: + + self.date_closed = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() + + if self.date_closed is not None and not self.is_closed: + + self.date_closed = None + + + def get_can_close(self, raise_exceptions = False ) -> bool: @@ -789,36 +862,6 @@ class TicketBase( def save(self, force_insert=False, force_update=False, using=None, update_fields=None): - related_model = self.get_related_model() - - if related_model is None: - - related_model = self - - if self.ticket_type != str(related_model._meta.sub_model_type).lower().replace(' ', '_'): - - self.ticket_type = str(related_model._meta.sub_model_type).lower().replace(' ', '_') - - if( - ( - self.status == self.TicketStatus.SOLVED - and self.get_can_resolve( raise_exceptions = False ) - ) - or self.status == self.TicketStatus.INVALID - ): - - self.is_solved = True - - - if self.date_solved is None and self.is_solved: - - self.date_solved = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() - - - if self.date_closed is None and self.is_closed: - - self.date_closed = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() - if( self.description != '' diff --git a/app/core/serializers/ticket.py b/app/core/serializers/ticket.py index db4e5609..8b90d05d 100644 --- a/app/core/serializers/ticket.py +++ b/app/core/serializers/ticket.py @@ -278,6 +278,16 @@ class ModelSerializer( def validate(self, attrs): + + if getattr(self.context['view'], 'action', '') in [ 'create' ]: + # Always set that the ticket was opened by user ho is making the request + + try: + attrs['opened_by'] = self.context['request'].user + except KeyError: + pass + + attrs = self.validate_field_milestone( attrs ) attrs = self.validate_field_external_system( attrs ) @@ -290,15 +300,13 @@ class ModelSerializer( status = int(attrs.get('status', 0)) - opened_by_id = int(attrs.get('opened_by_id', 0)) + opened_by_id = attrs.get('opened_by', 0) - if self.context.get('request', None): + if opened_by_id != 0: - request_user_id = int(self.context['request'].user.id) + opened_by_id = opened_by_id.id - else: - - request_user_id = 0 + request_user_id = int(self.context['request'].user.id) if opened_by_id == 0: @@ -364,6 +372,21 @@ class ModelSerializer( ) + elif ( + has_triage_permission + or has_import_permission + ): + + if( + ( + 'status' not in attrs + or attrs.get('status', 0) == self.Meta.model.TicketStatus.NEW + ) + and 'assigned_to' in attrs + ): + + attrs['status'] = self.Meta.model.TicketStatus.ASSIGNED + return attrs diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py index ad4b03a8..cf2838ed 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py @@ -18,29 +18,6 @@ from project_management.models.project_milestone import ( - -class MockView: - - _has_import: bool = False - """User Permission - - get_permission_required() sets this to `True` when user has import permission. - """ - - _has_purge: bool = False - """User Permission - - get_permission_required() sets this to `True` when user has purge permission. - """ - - _has_triage: bool = False - """User Permission - - get_permission_required() sets this to `True` when user has triage permission. - """ - - - class TicketBaseSerializerTestCases: @@ -114,11 +91,8 @@ class TicketBaseSerializerTestCases: 'permission_import_required': False, }, "opened_by": { - 'will_create': False, - 'exception_obj': ValidationError, - 'exception_code': 'required', - 'exception_code_key': None, - 'permission_import_required': True, + 'will_create': True, + 'permission_import_required': False, }, "subscribed_to": { 'will_create': True, @@ -224,12 +198,15 @@ class TicketBaseSerializerTestCases: request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password") + request.cls.other_user = User.objects.create_user(username="cafs_test_user_other", password="password") + yield with django_db_blocker.unblock(): request.cls.view_user.delete() + request.cls.other_user.delete() del request.cls.valid_data @@ -308,17 +285,23 @@ class TicketBaseSerializerTestCases: pass - def test_serializer_valid_data(self, create_serializer): + def test_serializer_valid_data(self, fake_view, create_serializer): """Serializer Validation Check Ensure that when creating an object with valid data, no validation error occurs. """ - view_set = MockView() + view_set = fake_view( + user = self.view_user, + _has_import = True, + _has_triage = True + ) + serializer = create_serializer( context = { + 'request': view_set.request, 'view': view_set, }, data = self.valid_data @@ -327,19 +310,22 @@ class TicketBaseSerializerTestCases: assert serializer.is_valid(raise_exception = True) - def test_serializer_valid_data_permission_import(self, create_serializer): + def test_serializer_valid_data_permission_import(self, fake_view, create_serializer): """Serializer Validation Check Ensure that when creating an object with valid data, no validation error occurs. when the user has permission import. """ - view_set = MockView() - - view_set._has_import = True + view_set = fake_view( + user = self.view_user, + _has_import = True, + _has_triage = False + ) serializer = create_serializer( context = { + 'request': view_set.request, 'view': view_set, }, data = self.valid_data @@ -349,7 +335,7 @@ class TicketBaseSerializerTestCases: - def test_serializer_valid_data_missing_field_raises_exception(self, parameterized, param_key_test_data, + def test_serializer_valid_data_missing_field_raises_exception(self, fake_view, parameterized, param_key_test_data, create_serializer, param_value, param_exception_obj, @@ -369,14 +355,16 @@ class TicketBaseSerializerTestCases: del valid_data[param_value] - view_set = MockView() - - view_set._has_import = True + view_set = fake_view( + user = self.view_user, + _has_import = True, + ) with pytest.raises(param_exception_obj) as err: serializer = create_serializer( context = { + 'request': view_set.request, 'view': view_set, }, data = valid_data, @@ -396,7 +384,7 @@ class TicketBaseSerializerTestCases: - def test_serializer_valid_data_missing_field_is_valid_permission_import(self, parameterized, param_key_test_data, + def test_serializer_valid_data_missing_field_is_valid_permission_import(self, fake_view, parameterized, param_key_test_data, create_serializer, param_value, param_will_create, @@ -412,12 +400,14 @@ class TicketBaseSerializerTestCases: del valid_data[param_value] - view_set = MockView() - - view_set._has_import = True + view_set = fake_view( + user = self.view_user, + _has_import = True, + ) serializer = create_serializer( context = { + 'request': view_set.request, 'view': view_set, }, data = valid_data @@ -510,6 +500,7 @@ class TicketBaseSerializerTestCases: ] ) def test_serializer_create_validation_status(self, + fake_view, create_serializer, name, param_permission_import, @@ -535,41 +526,26 @@ class TicketBaseSerializerTestCases: valid_data['status'] = status - view_set = MockView() + view_set = fake_view( + user = self.other_user, + _has_import = param_permission_import, + _has_triage = param_permission_triage + ) - view_set._has_import = param_permission_import - - view_set._has_triage = param_permission_triage - - - class MockRequest: - - class MockUser: - - id = 999999 - - pk = 999999 - - user = MockUser() - - mock_request = MockRequest() if param_is_owner: - mock_request.user.id = self.view_user.pk - - mock_request.user.pk = self.view_user.pk + view_set.request.user = self.view_user serializer = create_serializer( context = { + 'request': view_set.request, 'view': view_set, }, data = valid_data ) - serializer.context['request'] = mock_request - if type(expected_result) is not bool: with pytest.raises(ValidationError) as err: @@ -585,13 +561,13 @@ class TicketBaseSerializerTestCases: @pytest.fixture( scope = 'function' ) - def existing_ticket(self, db, create_serializer): + def existing_ticket(self, db, fake_view, create_serializer): - view_set = MockView() - - view_set._has_import = True - - view_set._has_triage = True + view_set = fake_view( + user = self.view_user, + _has_import = True, + _has_triage = True + ) valid_data = self.valid_data.copy() @@ -601,6 +577,7 @@ class TicketBaseSerializerTestCases: serializer = create_serializer( context = { + 'request': view_set.request, 'view': view_set, }, data = valid_data @@ -641,7 +618,7 @@ class TicketBaseSerializerTestCases: in values_validation_status_change_permission ] ) - def test_serializer_update_validation_status(self, + def test_serializer_update_validation_status(self, fake_view, create_serializer, existing_ticket, name, @@ -670,42 +647,28 @@ class TicketBaseSerializerTestCases: valid_data['status'] = status - view_set = MockView() - - view_set._has_import = param_permission_import - - view_set._has_triage = param_permission_triage - - - class MockRequest: - - class MockUser: - - id = 999999 - - pk = 999999 - - user = MockUser() - - mock_request = MockRequest() + view_set = fake_view( + user = self.other_user, + _has_import = param_permission_import, + _has_triage = param_permission_triage, + action = 'partial_update', + ) if param_is_owner: - mock_request.user.id = self.view_user.pk - - mock_request.user.pk = self.view_user.pk + view_set.request.user = self.view_user serializer = create_serializer( - instance = existing_ticket, + existing_ticket, context = { + 'request': view_set.request, 'view': view_set, }, data = valid_data, partial = True, ) - serializer.context['request'] = mock_request if type(expected_result) is not bool: @@ -721,6 +684,305 @@ class TicketBaseSerializerTestCases: + @pytest.fixture( scope = 'function' ) + def fresh_ticket_serializer(self, request, django_db_blocker, fake_view, create_serializer): + + view_set = fake_view( + user = request.cls.view_user + ) + + valid_data = request.cls.valid_data.copy() + + valid_data['title'] = 'ticktet with minimum fields' + + valid_data_keep_fields = [ + 'title', + 'organization', + 'description', + ] + + for field, value in valid_data.copy().items(): + + if field not in valid_data_keep_fields: + + del valid_data[field] + + + serializer = create_serializer( + context = { + 'request': view_set.request, + 'view': view_set, + }, + data = valid_data + ) + + + yield { + 'serializer': serializer, + 'valid_data': valid_data, + } + + with django_db_blocker.unblock(): + + if serializer.instance: + + serializer.instance.delete() + + + + def test_action_triage_user_assign_user_sets_status_assigned(self, model, fresh_ticket_serializer): + """Ticket Function Check + + Assigning the ticket must set the status to new + """ + + serializer = fresh_ticket_serializer['serializer'] + + serializer.initial_data['assigned_to'] = [ self.entity_user.id ] + + serializer.context['view']._has_triage = True + + serializer.is_valid(raise_exception = True) + + serializer.save() + + ticket = serializer.instance + + assert ticket.status == model.TicketStatus.ASSIGNED + + + + def test_action_triage_user_assign_user_and_status_no_status_update(self, model, fresh_ticket_serializer): + """Ticket Function Check + + Assigning the ticket and setting the status, does not set the status to assigned + """ + + serializer = fresh_ticket_serializer['serializer'] + + serializer.initial_data['assigned_to'] = [ self.entity_user.id ] + serializer.initial_data['status'] = model.TicketStatus.PENDING + + serializer.context['view']._has_triage = True + + serializer.is_valid(raise_exception = True) + + serializer.save() + + ticket = serializer.instance + + assert ticket.status == model.TicketStatus.PENDING + + + + date_action_clear_solved_ticket = [ + ('triage', True, False, 'is_solved', False), + ('triage', True, False, 'date_solved', None), + + ('ticket_owner', False, False, 'is_solved', False), + ('ticket_owner', False, False, 'date_solved', None), + + ('import', False, True, 'is_solved', False), + ('import', False, True, 'date_solved', None), + ] + + date_action_clear_closed_ticket = [ + ('triage', True, False, 'is_closed', False), + ('triage', True, False, 'date_closed', None), + + ('ticket_owner', False, False, 'is_closed', False), + ('ticket_owner', False, False, 'date_closed', None), + + ('import', False, True, 'is_closed', False), + ('import', False, True, 'date_closed', None), + ] + + + data_action_reopen_solved_ticket = [ + *date_action_clear_solved_ticket, + ] + + date_action_reopen_closed_ticket = [ + *date_action_clear_solved_ticket, + *date_action_clear_closed_ticket + ] + + + @pytest.mark.parametrize( + argnames = [ + 'name', + 'triage_user', + 'import_user', + 'field_name', + 'expected', + ], + argvalues = data_action_reopen_solved_ticket, + ids = [ + name +'_'+ field_name +'_'+str(expected).lower() for + name, + triage_user, + import_user, + field_name, + expected + in data_action_reopen_solved_ticket + ] + ) + def test_action_reopen_solved_ticket(self, + model, + fresh_ticket_serializer, + create_serializer, + name, + triage_user, + import_user, + field_name, + expected + ): + """Ticket Action Check + + When a ticket is reopened the following should occur: + - is_solved = False + - date_closed = None + + Only the following are supposed to be able to re-open a solved ticket: + - ticket owner + - triage user + - import user + """ + + # Create Solved Ticket + serializer = fresh_ticket_serializer['serializer'] + + serializer.initial_data['status'] = model.TicketStatus.SOLVED + + serializer.context['view']._has_triage = True + serializer.context['view']._has_import = True + + serializer.is_valid(raise_exception = True) + + serializer.save() + + ticket = serializer.instance + + # Confirm State + assert ticket.status == model.TicketStatus.SOLVED + assert ticket.is_solved + assert ticket.date_solved is not None + + # Re-Open Ticket + edit_serializer = create_serializer( + ticket, + context = serializer.context, + data = { + 'status': model.TicketStatus.NEW + }, + partial = True + ) + + edit_serializer.context['view']._has_triage = triage_user + edit_serializer.context['view']._has_import = import_user + + edit_serializer.is_valid(raise_exception = True) + + edit_serializer.save() + + ticket = edit_serializer.instance + + + assert getattr(ticket, field_name) == expected + + + + @pytest.mark.parametrize( + argnames = [ + 'name', + 'triage_user', + 'import_user', + 'field_name', + 'expected', + ], + argvalues = date_action_reopen_closed_ticket, + ids = [ + name +'_'+ field_name +'_'+str(expected).lower() for + name, + triage_user, + import_user, + field_name, + expected + in date_action_reopen_closed_ticket + ] + ) + def test_action_reopen_closed_ticket(self, + model, + fresh_ticket_serializer, + create_serializer, + name, + triage_user, + import_user, + field_name, + expected + ): + """Ticket Action Check + + When a ticket is reopened the following should occur: + - is_closed = False + - date_closed = None + - is_solved = False + - date_closed = None + + Only the following are supposed to be able to re-open a closed ticket: + - ticket owner + - triage user + - import user + """ + + # Create Closed Ticket + serializer = fresh_ticket_serializer['serializer'] + + serializer.initial_data['status'] = model.TicketStatus.CLOSED + + serializer.context['view']._has_triage = True + serializer.context['view']._has_import = True + + serializer.is_valid(raise_exception = True) + + serializer.save() + + ticket = serializer.instance + + # Confirm State + assert ticket.status == model.TicketStatus.CLOSED + assert ticket.is_closed + assert ticket.date_closed is not None + assert ticket.is_solved + assert ticket.date_solved is not None + + # Re-Open Ticket + edit_serializer = create_serializer( + ticket, + context = serializer.context, + data = { + 'status': model.TicketStatus.NEW + }, + partial = True + ) + + edit_serializer.context['view']._has_triage = triage_user + edit_serializer.context['view']._has_import = import_user + + edit_serializer.is_valid(raise_exception = True) + + edit_serializer.save() + + ticket = edit_serializer.instance + + + assert getattr(ticket, field_name) == expected + + + + + + class TicketBaseSerializerInheritedCases( TicketBaseSerializerTestCases, ): diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_api_fields.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_api_fields.py index 73b526e1..7012d70a 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_api_fields.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_api_fields.py @@ -62,6 +62,7 @@ class APITestCases( 'external_system': int(request.cls.model.Ticket_ExternalSystem.CUSTOM_1), 'impact': int(request.cls.model.TicketImpact.MEDIUM), 'priority': int(request.cls.model.TicketPriority.HIGH), + 'status': request.cls.model.TicketStatus.CLOSED, }) @@ -379,7 +380,9 @@ class APITestCases( 'real_start_date': '2025-04-16T00:00:03', 'real_finish_date': '2025-04-16T00:00:04', 'is_solved': True, + 'date_solved': '2025-05-12T02:30:01', 'is_closed': True, + 'date_closed': '2025-05-12T02:30:02', } url_ns_name = '_api_v2_ticket' diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py index fc1133fb..833038b4 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py @@ -850,7 +850,7 @@ class TicketBaseModelTestCases( - def test_function_called_clean_ticketcommentbase(self, model, mocker): + def test_function_called_clean_ticketbase(self, model, mocker): """Function Check Ensure function `TicketBase.clean` is called @@ -872,7 +872,7 @@ class TicketBaseModelTestCases( - def test_function_called_save_ticketcommentbase(self, model, mocker): + def test_function_called_save_ticketbase(self, model, mocker): """Function Check Ensure function `TicketBase.save` is called diff --git a/docs/projects/centurion_erp/user/core/tickets.md b/docs/projects/centurion_erp/user/core/tickets.md index 107541d3..a5c4c636 100644 --- a/docs/projects/centurion_erp/user/core/tickets.md +++ b/docs/projects/centurion_erp/user/core/tickets.md @@ -157,25 +157,10 @@ When using slash commands, there is only to be one slash command per line. All s summary: true -## Ticket Types +## Re-Opening a Ticket -::: app.core.models.ticket.ticket.Ticket.TicketType - options: - inherited_members: false - members: [] - show_bases: false - show_submodules: false - summary: true +To re-open a ticket is as simple as changing the status and saving. Not everyone can re-open a ticket, it depends upon the following: +- The user who raised the ticket can re-open the ticket only when the status is `SOLVED` -## Ticket Comments - -Within Centurion ERP the ticket comment model is common to all comment types. As with tickets the differences are the available fields, which depend upon comment type and permissions. - -::: app.core.models.ticket.ticket_comment.TicketComment.CommentType - options: - inherited_members: false - members: [] - show_bases: false - show_submodules: false - summary: true +- A User with `Triage` permission can re-open a ticket when the status is `SOLVED` or `CLOSED` regardless of who raised the ticket. From 5b133c951d0a906fd53eb5a6f6bc8f5e9de8c3fa Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 02:53:11 +0930 Subject: [PATCH 093/175] chore(test): Add a global fixture to simulate a viewset ref: #753 #723 #730 --- app/conftest.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/app/conftest.py b/app/conftest.py index 219a4ef3..bd7322f1 100644 --- a/app/conftest.py +++ b/app/conftest.py @@ -599,3 +599,80 @@ def recursearray() -> dict[dict, str, any]: } return _recursearray + + +@pytest.fixture( scope = 'function') +def fake_view(): + + + class MockView: + + class MockRequest: + + class MockUser: + + id: int + + pk: int + + user = None + + def __init__(cls, user = 0): + + if user != 0: + + cls.user = user + + # cls.user.id = user_id + # cls.user.pk = user_id + + + _has_import: bool + """User Permission + + get_permission_required() sets this to `True` when user has import permission. + """ + + _has_purge: bool + """User Permission + + get_permission_required() sets this to `True` when user has purge permission. + """ + + _has_triage: bool + """User Permission + + get_permission_required() sets this to `True` when user has triage permission. + """ + + action:str + """ The View Action + + This must be set to whatever is occuring + """ + + request: MockRequest + + def __init__(cls, + _has_import: bool = False, + _has_purge: bool = False, + _has_triage: bool = False, + action: str = 'create', + user = 0, + ): + + cls.request = cls.MockRequest( + user = user + ) + + cls._has_import = _has_import + cls._has_purge = _has_purge + cls._has_triage = _has_triage + cls.action = action + + + fake_view = MockView + + yield fake_view + + del fake_view From b0cb9f6fd090605108c40f290705b5d45046cbaf Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 04:04:32 +0930 Subject: [PATCH 094/175] chore(test): Add global support not to generate tests that are marked skip or class skip ref: #753 #739 --- app/conftest.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/conftest.py b/app/conftest.py index bd7322f1..1cf5a4e2 100644 --- a/app/conftest.py +++ b/app/conftest.py @@ -51,6 +51,16 @@ def pytest_generate_tests(metafunc): if {'parameterized'} <= set(metafunc.fixturenames): + for mark in metafunc.definition.own_markers: # Skip tests markd to skip + + if mark.name == 'skip': + return None + + for mark in getattr(metafunc.cls, 'pytestmark', []): # Skip test suite markd to skip + + if mark.name == 'skip': + return None + all_fixture_parameters = metafunc.fixturenames fixture_parameters += ['parameterized'] From 16299d480e9c9130dcb9142bd78311502753ae2a Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 04:05:53 +0930 Subject: [PATCH 095/175] test(core): TicketBase Description Slash command Checks ref: #744 #723 --- .../test_slash_command_related.py | 1100 +++++++++++++++-- 1 file changed, 1028 insertions(+), 72 deletions(-) diff --git a/app/core/tests/functional/slash_commands/test_slash_command_related.py b/app/core/tests/functional/slash_commands/test_slash_command_related.py index 3e597d88..02a9d20c 100644 --- a/app/core/tests/functional/slash_commands/test_slash_command_related.py +++ b/app/core/tests/functional/slash_commands/test_slash_command_related.py @@ -2028,7 +2028,6 @@ class SlashCommandsCommon: } - class SlashCommandsTicketTestCases( SlashCommandsCommon ): @@ -2038,7 +2037,340 @@ class SlashCommandsTicketTestCases( Requires a fixture called `Ticket` """ - pass + + def test_slash_command_ticket_single_line_with_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_with_command + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command in ticket.description + and command_obj in ticket.description + ) + + + + + def test_slash_command_ticket_single_line_command_own_line_lf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + + + + + def test_slash_command_ticket_single_line_command_own_line_crlf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_lf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_crlf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_blank_line_lf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_blank_line_crlf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + + + + def test_slash_command_ticket_single_line_command_own_line_blank_line_lf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + + + + def test_slash_command_ticket_single_line_command_own_line_blank_line_crlf_command_removed_from_description(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ( + param_slash_command not in ticket.description + and command_obj not in ticket.description + ) + # def test_slash_command_spend_ticket_duration_added(self, # ticket, @@ -2120,6 +2452,700 @@ class SlashCommandsTicketTestCases( + def test_slash_command_ticket_single_line_duration_not_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_with_command + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == 0 + + + + def test_slash_command_ticket_single_line_command_own_line_lf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.description == duration_in_seconds + + + + def test_slash_command_ticket_single_line_command_own_line_crlf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == duration_in_seconds + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_lf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == duration_in_seconds + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_crlf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == duration_in_seconds + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_blank_line_lf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == duration_in_seconds + + + + def test_slash_command_ticket_single_line_blank_line_command_own_line_blank_line_crlf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_blank_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == duration_in_seconds + + + + def test_slash_command_ticket_single_line_command_own_line_blank_line_lf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_lf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == duration_in_seconds + + + + def test_slash_command_ticket_single_line_command_own_line_blank_line_crlf_duration_added(self, + ticket, + parameterized, param_key_slash_command, param_name, + param_slash_command, + param_command_obj, + ): + """Slash command Check + + Ensure the command is removed from a comment + """ + + comment_text = self.single_line_command_own_line_blank_line_crlf + + assert 'COMMAND' in comment_text + # COMMAND must be in ticket comment so it can be constructed + + command_obj = str(param_command_obj).replace( + 'EXISTINGTICKET', str(self.existing_ticket.id) + ) + + durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + hour = durations['hour'] + + if not hour: + hour = 0 + + else: + hour = str(durations['hour']).replace('h', '') + + hour = (int(hour) * 60) * 60 + + + minute = durations['minute'] + + if not minute: + minute = 0 + + else: + minute = str(durations.get('minute', 0)).replace('m', '') + + minute = int(minute) * 60 + + + second = durations['second'] + + if not second: + second = 0 + else: + second = str(durations['second']).replace('s', '') + + second = int(second) + + duration_in_seconds = hour + minute + second + + ticket.description = str( + comment_text.replace( + 'COMMAND', '/' + param_slash_command + ' ' + command_obj + ) + ) + + + ticket.save() + + + assert ticket.duration == duration_in_seconds + + + + # def test_slash_command_spend_ticket_comment_duration_added(self, + # ticket_comment, + # parameterized, param_key_slash_command, param_name, + # param_slash_command, + # param_command_obj, + # param_spend, + # ): + # """Slash command Check + + # Ensure the `spend` slash command adds the duration to the tickets + # duration field. + # """ + + # comment_text = self.single_line_command_own_line_blank_line_crlf + + # assert 'COMMAND' in comment_text + # # COMMAND must be in ticket comment so it can be constructed + + # command_obj = str(param_command_obj).replace( + # 'EXISTINGTICKET', str(self.existing_ticket.id) + # ) + + + # durations = re.match('(?P\d+h)?\s?(?P\d+m)?\s?(?P\d+s)?', param_command_obj).groupdict() + + # hour = durations['hour'] + + # if not hour: + # hour = 0 + + # else: + # hour = str(durations['hour']).replace('h', '') + + # hour = (int(hour) * 60) * 60 + + + # minute = durations['minute'] + + # if not minute: + # minute = 0 + + # else: + # minute = str(durations.get('minute', 0)).replace('m', '') + + # minute = int(minute) * 60 + + + # second = durations['second'] + + # if not second: + # second = 0 + # else: + # second = str(durations['second']).replace('s', '') + + # second = int(second) + + # duration_in_seconds = hour + minute + second + + # ticket_comment.body = str( + # comment_text.replace( + # 'COMMAND', '/' + param_slash_command + ' ' + command_obj + # ) + # ) + + + # ticket_comment.save() + + + # assert ticket_comment.duration == duration_in_seconds + + + + class SlashCommandsTicketCommentTestCases( SlashCommandsCommon ): @@ -2469,62 +3495,6 @@ class SlashCommandsTicketCommentTestCases( ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # def test_slash_command_spend_ticket_duration_added(self, # ticket, # parameterized, param_key_slash_command, param_name, @@ -3237,20 +4207,6 @@ class SlashCommandsTicketCommentTestCases( - - - - - - - - - - - - - - # def test_slash_command_spend_ticket_comment_duration_added(self, # ticket_comment, # parameterized, param_key_slash_command, param_name, From 01b4a681da23a1039ed6b832d4813a5e5ef94b46 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 04:07:19 +0930 Subject: [PATCH 096/175] test(core): SKIP Tests TicketBase Description Slash command Checks Required until the slash commands comment creation uses the new ticketcommentbase model. ref: #744 #723 #564 #746 #747 --- .../functional/slash_commands/test_slash_command_related.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/core/tests/functional/slash_commands/test_slash_command_related.py b/app/core/tests/functional/slash_commands/test_slash_command_related.py index 02a9d20c..edba7c29 100644 --- a/app/core/tests/functional/slash_commands/test_slash_command_related.py +++ b/app/core/tests/functional/slash_commands/test_slash_command_related.py @@ -2028,6 +2028,8 @@ class SlashCommandsCommon: } + +@pytest.mark.skip( reason = 'Awaiting Ticket Refactor') class SlashCommandsTicketTestCases( SlashCommandsCommon ): From 764f1b20d9543dacfd786297b25718abe03afbc0 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 04:46:44 +0930 Subject: [PATCH 097/175] test(core): Ensure that a ticket milestone comes from the same assigned project ref: #744 #723 --- .github/ISSUE_TEMPLATE/new_model.md | 29 +++++-- .../test_functional_ticket_base_serializer.py | 83 +++++++++++++++++++ 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/new_model.md b/.github/ISSUE_TEMPLATE/new_model.md index d2888006..2b2359b9 100644 --- a/.github/ISSUE_TEMPLATE/new_model.md +++ b/.github/ISSUE_TEMPLATE/new_model.md @@ -77,16 +77,17 @@ Describe in detail the following: ### 🧪 Tests - Unit Tests + - [ ] API Render (fields) - [ ] [Model](https://nofusscomputing.com/projects/centurion_erp/development/models/#tests) - - [ ] Serializer - [ ] ViewSet - Function Test - - [ ] ViewSet + - [ ] History API Render (fields) + - [ ] History Entries - [ ] API Metadata - [ ] API Permissions - - [ ] API Render (fields) - - [ ] History Entries - - [ ] History API Render (fields) + - [ ] Model + - [ ] Serializer + - [ ] ViewSet ## ✅ Requirements @@ -95,6 +96,24 @@ A Requirement is a must have. In addition will also be tested. - [ ] Must have a [model_tag](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference) + + +- Functional Requirements + + +- Non-Functional Requirements + + --- diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py index cf2838ed..bde943d2 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py @@ -241,6 +241,7 @@ class TicketBaseSerializerTestCases: ) request.cls.valid_data.update({ + 'organization': request.cls.organization, 'category': TicketCategory.objects.create( organization = request.cls.organization, name = 'a category' @@ -262,10 +263,28 @@ class TicketBaseSerializerTestCases: }) + project_two = Project.objects.create( + organization = request.cls.organization, + name = 'project_two' + ) + + request.cls.project_milestone_two = ProjectMilestone.objects.create( + organization = request.cls.organization, + name = 'project milestone two', + project = project_two + ) + + + + yield with django_db_blocker.unblock(): + request.cls.project_milestone_two.delete() + + project_two.delete() + request.cls.entity_user.delete() parent_ticket.delete() @@ -335,6 +354,70 @@ class TicketBaseSerializerTestCases: + def test_serializer_valid_data_milestone_from_different_project_not_valid(self, fake_view, create_serializer): + """Serializer Validation Check + + Ensure that when creating an object with valid data, no validation + error occurs. + """ + + valid_data = self.valid_data.copy() + + valid_data['milestone'] = self.project_milestone_two.id + + view_set = fake_view( + user = self.view_user, + _has_import = True, + _has_triage = True + ) + + + serializer = create_serializer( + context = { + 'request': view_set.request, + 'view': view_set, + }, + data = valid_data + ) + + assert not serializer.is_valid(raise_exception = False) + + + + def test_serializer_valid_data_milestone_from_different_project_raises_exception(self, fake_view, create_serializer): + """Serializer Validation Check + + Ensure that when creating an object with valid data, no validation + error occurs. + """ + + valid_data = self.valid_data.copy() + + valid_data['milestone'] = self.project_milestone_two.id + + view_set = fake_view( + user = self.view_user, + _has_import = True, + _has_triage = True + ) + + + with pytest.raises(ValidationError) as err: + + serializer = create_serializer( + context = { + 'request': view_set.request, + 'view': view_set, + }, + data = valid_data + ) + + serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()['milestone'][0] == 'milestone_same_project' + + + def test_serializer_valid_data_missing_field_raises_exception(self, fake_view, parameterized, param_key_test_data, create_serializer, param_value, From 0fa7ffa34a916c43793d1b7c1432676d2560db14 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 18:55:25 +0930 Subject: [PATCH 098/175] chore: correct dev log path to be /log ref: #754 --- app/app/settings.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/app/settings.py b/app/app/settings.py index 6da519f3..588d5015 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -82,10 +82,10 @@ FEATURE_FLAG_OVERRIDES = None # Feature Flags to override fetched feature flags LOG_FILES = { # defaults for devopment. docker includes settings has correct locations - "centurion": "../log/centurion.log", - "weblog": "../log/weblog.log", - "rest_api": "../log/rest_api.log", - "catch_all":"../log/catch-all.log" + "centurion": "log/centurion.log", + "weblog": "log/weblog.log", + "rest_api": "log/rest_api.log", + "catch_all":"log/catch-all.log" } CENTURION_LOGGING = { @@ -173,6 +173,10 @@ METRICS_ENABLED = False # Enable Metrics METRICS_EXPORT_PORT = 8080 # Port to serve metrics on METRICS_MULTIPROC_DIR = '/tmp/prometheus' # path the metrics from multiple-process' save to + +RUNNING_TESTS = 'test' in str(sys.argv) + + # django setting. CACHES = { 'default': { @@ -457,8 +461,6 @@ DATETIME_FORMAT = 'j N Y H:i:s' # Settings for unit tests # -RUNNING_TESTS = 'test' in str(sys.argv) - if RUNNING_TESTS: SECRET_KEY = 'django-insecure-tests_are_being_run' @@ -490,11 +492,11 @@ CENTURION_LOGGING['handlers']['file_rest_api']['filename'] = LOG_FILES['rest_api CENTURION_LOGGING['handlers']['file_catch_all']['filename'] = LOG_FILES['catch_all'] -if str(CENTURION_LOGGING['handlers']['file_centurion']['filename']).startswith('../log'): +if str(CENTURION_LOGGING['handlers']['file_centurion']['filename']).startswith('log') and not RUNNING_TESTS: - if not os.path.exists('../log'): # Create log dir + if not os.path.exists(os.path.join(BASE_DIR, 'log')): # Create log dir - os.makedirs('../log') + os.makedirs(os.path.join(BASE_DIR, 'log')) if DEBUG: @@ -511,8 +513,9 @@ if DEBUG: ] -# Setup Logging -LOGGING = CENTURION_LOGGING +if not RUNNING_TESTS: + # Setup Logging + LOGGING = CENTURION_LOGGING if METRICS_ENABLED: From 899df95994b1901222fa70788e56ecba5e2edacc Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 19:55:37 +0930 Subject: [PATCH 099/175] feat(api): Add exception logging to ViewSetCommon ref: #754 #752 --- app/api/viewsets/common.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index 27779c49..e2ce676c 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -89,6 +89,8 @@ class Create( status = 501 ) + self.get_log().exception(e) + else: response = Response( @@ -139,6 +141,8 @@ class Destroy( status = 501 ) + self.get_log().exception(e) + else: response = Response( @@ -190,6 +194,8 @@ class List( status = 501 ) + self.get_log().exception(e) + else: response = Response( From 4ad6c10a648f411104359a7a98601b19056ba56b Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 20:10:28 +0930 Subject: [PATCH 100/175] chore(core): Remove commented code ref: #754 #726 --- app/core/viewsets/ticket_comment.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/core/viewsets/ticket_comment.py b/app/core/viewsets/ticket_comment.py index 651c789f..85e13591 100644 --- a/app/core/viewsets/ticket_comment.py +++ b/app/core/viewsets/ticket_comment.py @@ -1,35 +1,17 @@ -# from django.db.models import Q -# from django.shortcuts import get_object_or_404 import importlib from django.apps import apps from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse, PolymorphicProxySerializer -# from rest_framework import generics, viewsets -# from rest_framework.response import Response - -# from access.mixin import OrganizationMixin - -# from api.views.mixin import OrganizationPermissionAPI from api.viewsets.common import SubModelViewSet -# from core import exceptions as centurion_exceptions -# from core.models.ticket_base import TicketBase from core.models.ticket_comment_base import ( TicketBase, TicketCommentBase ) -# from core.serializers.ticket_comment import ( -# # Ticket, -# # TicketComment, -# TicketCommentBase, -# # ModelSerializer, -# # ViewSerializer, -# ) -# from settings.models.user_settings import UserSettings def spectacular_request_serializers( serializer_type = 'Model'): From 1cb028c273e079ed35fb8e21318cdbc2347010b1 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 20:11:01 +0930 Subject: [PATCH 101/175] feat(core): Setup model to meet requirements ref: #754 #726 --- app/core/models/ticket_comment_base.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/core/models/ticket_comment_base.py b/app/core/models/ticket_comment_base.py index 35856978..50f8add4 100644 --- a/app/core/models/ticket_comment_base.py +++ b/app/core/models/ticket_comment_base.py @@ -1,3 +1,5 @@ +import datetime + from django.apps import apps from django.db import models @@ -255,16 +257,17 @@ class TicketCommentBase( def clean(self): + + if not self.organization: + + self.organization = self.ticket.organization + + if not self.is_template: if self.is_closed and self.date_closed is None: - raise centurion_exception.ValidationError( - detail = { - 'date_closed': 'Ticket has been marked as closed and field date_closed is empty.' - }, - code = 'ticket_closed_no_date' - ) + self.date_closed = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() if self.comment_type != self._meta.sub_model_type: @@ -397,8 +400,6 @@ class TicketCommentBase( def save(self, force_insert=False, force_update=False, using=None, update_fields=None): - self.organization = self.ticket.organization - body = self.body self.body = self.slash_command(self.body) From 7f49fb2d0efeef74e1ee07416a5032a154627d7b Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 12 May 2025 20:11:11 +0930 Subject: [PATCH 102/175] feat(core): Setup serializer to meet requirements ref: #754 #726 --- app/core/models/ticket_comment_base.py | 5 +- app/core/serializers/ticket_comment.py | 160 +++++++++++++----- ...est_unit_ticket_comment_base_api_fields.py | 2 +- .../test_unit_ticket_comment_base_model.py | 26 +-- 4 files changed, 137 insertions(+), 56 deletions(-) diff --git a/app/core/models/ticket_comment_base.py b/app/core/models/ticket_comment_base.py index 50f8add4..c5f2da83 100644 --- a/app/core/models/ticket_comment_base.py +++ b/app/core/models/ticket_comment_base.py @@ -257,9 +257,10 @@ class TicketCommentBase( def clean(self): + try: + self.organization - if not self.organization: - + except: self.organization = self.ticket.organization diff --git a/app/core/serializers/ticket_comment.py b/app/core/serializers/ticket_comment.py index cc1e5da4..50708e3f 100644 --- a/app/core/serializers/ticket_comment.py +++ b/app/core/serializers/ticket_comment.py @@ -6,12 +6,12 @@ from rest_framework import serializers from drf_spectacular.utils import extend_schema_serializer +from access.serializers.entity import BaseSerializer as EntityBaseSerializer from access.serializers.organization import OrganizationBaseSerializer from api.serializers import common from api.exceptions import UnknownTicketType -from app.serializers.user import UserBaseSerializer from core import exceptions as centurion_exceptions from core import fields as centurion_field @@ -114,8 +114,6 @@ class ModelSerializer( model = TicketCommentBase - fields = '__all__' - fields = [ 'id', 'organization', @@ -174,21 +172,131 @@ class ModelSerializer( ] - is_triage: bool = False - """ If the serializers is a Triage serializer""" + + def __init__(self, *args, **kwargs): + + super().__init__(*args, **kwargs) + + if kwargs['context']['view'].action in ['create', 'partial_update', 'update']: + + self.fields.fields['parent'].write_only = True + self.fields.fields['ticket'].write_only = True + self.fields.fields['user'].write_only = True + + if self.context.get('view', None) is not None: + + read_only_fields = [ + 'id', + 'display_name', + '_urls', + ] + + if not self.context['view']._has_import: + + read_only_fields += [ + 'created', + 'modified', + 'organization' + 'external_system', + 'external_ref', + 'duration', + ] + + if( + not self.context['view']._has_import + and not self.context['view']._has_triage + ): + + read_only_fields += [ + 'category', + 'source', + ] + + self.Meta.read_only_fields = read_only_fields + + + + def validate_triage(self, attrs): + + return attrs + + + def validate_new_comment(self, attrs): + + # attrs['user'] = self.context['request'].user + + + if 'ticket_id' in self.context['view'].kwargs: + + attrs['ticket_id'] = int(self.context['view'].kwargs['ticket_id']) + + + if 'parent_id' in self.context['view'].kwargs: + + attrs['parent_id'] = int(self.context['view'].kwargs['parent_id']) + + comment = self.Meta.model.objects.filter( id = attrs['parent_id'] ) + + + if list(comment)[0].parent_id: + + raise centurion_exceptions.ValidationError( + detail = { + 'parent': 'Replying to a discussion reply is not possible' + }, + code = 'single_discussion_replies_only' + ) + + else: + + raise centurion_exceptions.ValidationError( + detail = { + 'parent': 'Replying to a discussion reply is not possible' + }, + code = 'single_discussion_replies_only' + ) + + return attrs + + + + def validate_update_comment(self, attrs): + + if self.instance.user != self.context['request'].user: # Owner Edit + + if 'private' in attrs: + + if self.instance.private: + + raise centurion_exceptions.ValidationError( + detail = { + 'private': 'Once a comment is made private it can\'t be undone.' + }, + code = 'owner_cant_remove_private' + ) + + + return attrs + + + def validate(self, attrs): attrs['comment_type'] = self.context['view'].model._meta.sub_model_type - if attrs['comment_type'] == 'comment': + if self.context['view']._has_triage: - attrs['is_closed'] = True - attrs['date_closed'] = datetime.datetime.now(tz=datetime.timezone.utc).replace(microsecond=0).isoformat() + attrs = self.validate_triage( attrs ) - if self.is_triage: - attrs = self.validate_triage(attrs) + if self.context['view'].action == 'create': + + attrs = self.validate_new_comment( attrs ) + + elif self.context['view'].action in [ 'partial_update', 'update' ]: + + attrs = self.validate_update_comment( attrs ) return attrs @@ -202,36 +310,6 @@ class ModelSerializer( is_valid = super().is_valid(raise_exception=raise_exception) - if self.context['view'].action == 'create': - - if 'ticket_id' in self._kwargs['context']['view'].kwargs: - - self.validated_data['ticket_id'] = int(self._kwargs['context']['view'].kwargs['ticket_id']) - - if 'parent_id' in self._kwargs['context']['view'].kwargs: - - self.validated_data['parent_id'] = int(self._kwargs['context']['view'].kwargs['parent_id']) - - comment = self.Meta.model.objects.filter( id = self.validated_data['parent_id'] ) - - if list(comment)[0].parent_id: - - raise centurion_exceptions.ValidationError( - detail = { - 'parent': 'Replying to a discussion reply is not possible' - }, - code = 'single_discussion_replies_only' - ) - - else: - - raise centurion_exceptions.ValidationError( - detail = { - 'ticket': 'Ticket is a required field' - }, - code = 'required' - ) - return is_valid @@ -249,4 +327,4 @@ class ViewSerializer(ModelSerializer): ticket = TicketBaseBaseSerializer() - user = UserBaseSerializer() + user = EntityBaseSerializer() diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py index ef5321aa..e14d54db 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py @@ -301,7 +301,7 @@ class TicketCommentBaseAPITestCases( 'expected': str }, 'user.url': { - 'expected': Hyperlink + 'expected': str }, 'is_closed': { diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py index 0457c6c1..7380b87f 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py @@ -530,25 +530,27 @@ class TicketCommentBaseModelPyTest( ): - def test_function_clean_validation_close_raises_exception(self, ticket): - """Function Check + # def test_function_clean_validation_close_raises_exception(self, ticket): + # """Function Check - Ensure function `clean` does validation - """ + # Ensure function `clean` does validation + # """ - valid_data = self.kwargs_create_item.copy() + # valid_data = self.kwargs_create_item.copy() - valid_data['ticket'] = ticket + # valid_data['ticket'] = ticket - del valid_data['date_closed'] + # valid_data['external_ref'] = 9842 - with pytest.raises(ValidationError) as err: + # del valid_data['date_closed'] - self.model.objects.create( - **valid_data - ) + # with pytest.raises(ValidationError) as err: - assert err.value.get_codes()['date_closed'] == 'ticket_closed_no_date' + # self.model.objects.create( + # **valid_data + # ) + + # assert err.value.get_codes()['date_closed'] == 'ticket_closed_no_date' def test_function_save_called_slash_command(self, model, mocker, ticket): From c8df4b21a0e87660eceb0acf62d8816a326d2c7f Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:19:10 +0930 Subject: [PATCH 103/175] feat(core): New model TicketCommentAction ref: #754 #736 --- app/core/models/__init__.py | 1 + app/core/models/ticket_comment_action.py | 55 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 app/core/models/ticket_comment_action.py diff --git a/app/core/models/__init__.py b/app/core/models/__init__.py index 7a1e5ce2..92b51546 100644 --- a/app/core/models/__init__.py +++ b/app/core/models/__init__.py @@ -1,3 +1,4 @@ from .history import History +from . import ticket_comment_action from . import ticket_comment_base from . import ticket_comment_solution diff --git a/app/core/models/ticket_comment_action.py b/app/core/models/ticket_comment_action.py new file mode 100644 index 00000000..06aaccf9 --- /dev/null +++ b/app/core/models/ticket_comment_action.py @@ -0,0 +1,55 @@ +import datetime + + +from core import exceptions as centurion_exception +from core.models.ticket_comment_base import TicketCommentBase + + + +class TicketCommentAction( + TicketCommentBase, +): + + class Meta: + + ordering = [ + 'id' + ] + + permissions = [ + ('import_ticketcommentaction', 'Can import ticket action comment.'), + ] + + sub_model_type = 'action' + + verbose_name = "Ticket Comment Action" + + verbose_name_plural = "Ticket Comment Actions" + + + def clean(self): + + self.is_closed = True + + super().clean() + + + + # def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + + # super().save(force_insert = force_insert, force_update = force_update, using = using, update_fields = update_fields) + + # self.ticket.is_solved = + + # self.ticket.date_solved = self.date_closed + + # self.ticket.status = self.ticket.TicketStatus.SOLVED + + # self.ticket.save() + + # # clear comment cache + # if hasattr(self.ticket, '_ticket_comments'): + + # del self.ticket._ticket_comments + + From 003cad1f58ec9fcf0ab9ed19087b208823cb4353 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:19:23 +0930 Subject: [PATCH 104/175] feat(core): Migrations for model TicketCommentAction ref: #754 #736 --- .../migrations/0025_ticketcommentaction.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/core/migrations/0025_ticketcommentaction.py diff --git a/app/core/migrations/0025_ticketcommentaction.py b/app/core/migrations/0025_ticketcommentaction.py new file mode 100644 index 00000000..b1360e3f --- /dev/null +++ b/app/core/migrations/0025_ticketcommentaction.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.9 on 2025-05-13 09:48 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0024_alter_ticketbase_opened_by_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='TicketCommentAction', + fields=[ + ('ticketcommentbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.ticketcommentbase')), + ], + options={ + 'verbose_name': 'Ticket Comment Action', + 'verbose_name_plural': 'Ticket Comment Actions', + 'ordering': ['id'], + 'permissions': [('import_ticketcommentaction', 'Can import ticket action comment.')], + 'sub_model_type': 'action', + }, + bases=('core.ticketcommentbase',), + ), + ] From 6169324ffd06714dab2052a8c647da9e89cbd064 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:19:37 +0930 Subject: [PATCH 105/175] feat(core): Serializer for model TicketCommentAction ref: #754 #736 --- .github/ISSUE_TEMPLATE/new_model.md | 2 ++ app/core/serializers/ticket_comment_action.py | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 app/core/serializers/ticket_comment_action.py diff --git a/.github/ISSUE_TEMPLATE/new_model.md b/.github/ISSUE_TEMPLATE/new_model.md index 2b2359b9..a609a054 100644 --- a/.github/ISSUE_TEMPLATE/new_model.md +++ b/.github/ISSUE_TEMPLATE/new_model.md @@ -33,6 +33,8 @@ Describe in detail the following: - [ ] 🛠️ Migrations added +- [ ] ♻️ Serializer Created + - [ ] 🔄 [ViewSet Created](https://nofusscomputing.com/projects/centurion_erp/development/views/) - [ ] 🔗 URL Route Added diff --git a/app/core/serializers/ticket_comment_action.py b/app/core/serializers/ticket_comment_action.py new file mode 100644 index 00000000..cdf2842e --- /dev/null +++ b/app/core/serializers/ticket_comment_action.py @@ -0,0 +1,31 @@ +from drf_spectacular.utils import extend_schema_serializer + +from core.models.ticket_comment_action import TicketCommentAction +from core.serializers.ticket_comment import ( + BaseSerializer, + ModelSerializer as TicketCommentBaseModelSerializer, + ViewSerializer as TicketCommentBaseViewSerializer +) + + + +@extend_schema_serializer(component_name = 'TicketCommentActionModelSerializer') +class ModelSerializer( + TicketCommentBaseModelSerializer, + BaseSerializer, +): + + + class Meta(TicketCommentBaseModelSerializer.Meta): + + model = TicketCommentAction + + + +@extend_schema_serializer(component_name = 'TicketCommentActionViewSerializer') +class ViewSerializer( + TicketCommentBaseViewSerializer, + ModelSerializer, +): + + pass From 969b4d22fc48ea08f09e07a500693638cf0b292d Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:42:31 +0930 Subject: [PATCH 106/175] feat(core): Remove add, change and delete permissions for model TicketCommentAction from permission selector ref: #754 #736 --- app/access/functions/permissions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/access/functions/permissions.py b/app/access/functions/permissions.py index 3b65f822..458c6470 100644 --- a/app/access/functions/permissions.py +++ b/app/access/functions/permissions.py @@ -37,14 +37,17 @@ def permission_queryset(): 'add_history', 'add_organization', 'add_taskresult', + 'add_ticketcommentaction', 'change_checkin', 'change_history', 'change_organization', 'change_taskresult', + 'change_ticketcommentaction', 'delete_checkin', 'delete_history', 'delete_organization', 'delete_taskresult', + 'delete_ticketcommentaction', 'view_checkin', 'view_history', ] From 21a1974ba1b574889e6a7e348a96cf2ce2e3db0a Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:43:13 +0930 Subject: [PATCH 107/175] test(core): Interim Functional model Test Case TicketCommentAction ref: #754 #736 --- .../ticket_comment_action/conftest.py | 14 ++++++++++ ..._functional_ticket_comment_action_model.py | 26 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 app/core/tests/functional/ticket_comment_action/conftest.py create mode 100644 app/core/tests/functional/ticket_comment_action/test_functional_ticket_comment_action_model.py diff --git a/app/core/tests/functional/ticket_comment_action/conftest.py b/app/core/tests/functional/ticket_comment_action/conftest.py new file mode 100644 index 00000000..abbe990a --- /dev/null +++ b/app/core/tests/functional/ticket_comment_action/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from core.models.ticket_comment_action import TicketCommentAction + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = TicketCommentAction + + yield request.cls.model + + del request.cls.model diff --git a/app/core/tests/functional/ticket_comment_action/test_functional_ticket_comment_action_model.py b/app/core/tests/functional/ticket_comment_action/test_functional_ticket_comment_action_model.py new file mode 100644 index 00000000..ae2b9501 --- /dev/null +++ b/app/core/tests/functional/ticket_comment_action/test_functional_ticket_comment_action_model.py @@ -0,0 +1,26 @@ +from core.tests.functional.ticket_comment_base.test_functional_ticket_comment_base_model import TicketCommentBaseModelInheritedTestCases + + +class TicketCommentActionModelTestCases( + TicketCommentBaseModelInheritedTestCases +): + pass + + + # check comment status is closed + + + +class TicketCommentActionModelInheritedTestCases( + TicketCommentActionModelTestCases +): + + pass + + + +class TicketCommentActionModelPyTest( + TicketCommentActionModelTestCases +): + + pass From 4ee5f349f01277762ef1eaeeb4a9e05a9b136362 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:51:29 +0930 Subject: [PATCH 108/175] test(core): Unit API Render Test Cases for TicketCommentAction model ref: #754 #736 --- .../unit/ticket_comment_action/conftest.py | 14 +++++++++ ...t_unit_ticket_comment_action_api_fields.py | 31 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_action/conftest.py create mode 100644 app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_api_fields.py diff --git a/app/core/tests/unit/ticket_comment_action/conftest.py b/app/core/tests/unit/ticket_comment_action/conftest.py new file mode 100644 index 00000000..abbe990a --- /dev/null +++ b/app/core/tests/unit/ticket_comment_action/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from core.models.ticket_comment_action import TicketCommentAction + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = TicketCommentAction + + yield request.cls.model + + del request.cls.model diff --git a/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_api_fields.py b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_api_fields.py new file mode 100644 index 00000000..fa7d48fa --- /dev/null +++ b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_api_fields.py @@ -0,0 +1,31 @@ +from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_api_fields import ( + TicketCommentBaseAPIInheritedCases +) + + + +class TicketCommentActionAPITestCases( + TicketCommentBaseAPIInheritedCases, +): + + parameterized_test_data = {} + + kwargs_create_item: dict = {} + + + +class TicketCommentActionAPIInheritedCases( + TicketCommentActionAPITestCases, +): + + kwargs_create_item: dict = {None} + + model = None + + + +class TicketCommentActionAPIPyTest( + TicketCommentActionAPITestCases, +): + + pass From c4ee70659156c1f9487af86132099a4dc6bdb2e1 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:51:39 +0930 Subject: [PATCH 109/175] test(core): Unit model Test Cases for TicketCommentAction model ref: #754 #736 --- .../test_unit_ticket_comment_action_model.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py diff --git a/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py new file mode 100644 index 00000000..6163f288 --- /dev/null +++ b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py @@ -0,0 +1,109 @@ +from rest_framework.exceptions import ValidationError + +from core.models.ticket_comment_action import TicketCommentAction +from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_model import ( + TicketCommentBaseModelInheritedCases +) + + +class TicketCommentActionModelTestCases( + TicketCommentBaseModelInheritedCases, +): + + sub_model_type = 'action' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + kwargs_create_item: dict = { + 'comment_type': sub_model_type, + } + + + def test_class_inherits_ticketcommentaction(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory + """ + + assert issubclass(self.model, TicketCommentAction) + + + + def test_function_called_clean_ticketcommentaction(self, model, mocker, ticket): + """Function Check + + Ensure function `TicketCommentBase.clean` is called + """ + + spy = mocker.spy(TicketCommentAction, 'clean') + + valid_data = self.kwargs_create_item.copy() + + valid_data['ticket'] = ticket + + del valid_data['external_system'] + del valid_data['external_ref'] + + model.objects.create( + **valid_data + ) + + assert spy.assert_called_once + + + def test_attribute_value_permissions_has_triage(self): + """Attribute Check + + This test case is a duplicate of a test with the same name. + This type of ticket comment does not have a triage permission. + + Ensure attribute `Meta.permissions` value contains permission + `triage` + """ + pass + + + + def test_attribute_value_permissions_has_purge(self): + """Attribute Check + + This test case is a duplicate of a test with the same name. + This type of ticket comment does not have a triage permission. + + Ensure attribute `Meta.permissions` value contains permission + `purge` + """ + pass + + + + + +class TicketCommentActionModelInheritedCases( + TicketCommentActionModelTestCases, +): + """Sub-Ticket Test Cases + + Test Cases for Ticket models that inherit from model TicketCommentAction + """ + + kwargs_create_item: dict = {} + + model = None + + + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ + + + +class TicketCommentActionModelPyTest( + TicketCommentActionModelTestCases, +): + + pass From 7c35a2d42700785d1154aa736e79dd535f91454b Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 19:52:04 +0930 Subject: [PATCH 110/175] test(core): Unit viewset Test Cases for TicketCommentAction model ref: #754 #736 --- .../unit/ticket_comment_action/__init__.py | 0 ...test_unit_ticket_comment_action_viewset.py | 39 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 app/core/tests/unit/ticket_comment_action/__init__.py create mode 100644 app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_viewset.py diff --git a/app/core/tests/unit/ticket_comment_action/__init__.py b/app/core/tests/unit/ticket_comment_action/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_viewset.py b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_viewset.py new file mode 100644 index 00000000..e3b61f67 --- /dev/null +++ b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_viewset.py @@ -0,0 +1,39 @@ +from django.test import TestCase + +from core.models.ticket_comment_action import TicketCommentAction +from core.tests.unit.ticket_comment_base.test_unit_ticket_comment_base_viewset import TicketCommentBaseViewsetInheritedCases + + + +class TicketCommentActionViewsetTestCases( + TicketCommentBaseViewsetInheritedCases, +): + + + @classmethod + def setUpTestData(self): + + self.model = TicketCommentAction + + super().setUpTestData() + + + +class TicketCommentActionViewsetInheritedCases( + TicketCommentActionViewsetTestCases, +): + """Test Suite for Sub-Models of TicketBase + + Use this Test suit if your sub-model inherits directly from TicketCommentAction. + """ + + model: str = None + """name of the model to test""" + + + +class TicketCommentActionViewsetTest( + TicketCommentActionViewsetTestCases, + TestCase, +): + pass From 1946c7aa88d5a32317ca8b04c8430c190c9c5dbb Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 23:29:02 +0930 Subject: [PATCH 111/175] fix(api): Ensure multi-nested searching for sub-models works ref: #754 --- app/api/viewsets/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index e2ce676c..bfee9eab 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -833,7 +833,6 @@ class SubModelViewSet( related_model = self.related_objects(model = related_object.related_model, model_kwarg = model_kwarg) - break if related_model is None: From 820755b9e05c9e01f5e8fb2cfd45276e284d6e0f Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 23:30:19 +0930 Subject: [PATCH 112/175] chore(test): introduce further randomness to class fixtures so as not to create duplicate errors ref: #754 --- app/api/tests/unit/test_unit_api_fields.py | 8 +++++--- app/conftest.py | 9 +++++++-- .../test_functional_ticket_comment_base_model.py | 4 ++++ .../test_unit_ticket_comment_base_model.py | 5 ++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/api/tests/unit/test_unit_api_fields.py b/app/api/tests/unit/test_unit_api_fields.py index c7745164..c9dc6a3a 100644 --- a/app/api/tests/unit/test_unit_api_fields.py +++ b/app/api/tests/unit/test_unit_api_fields.py @@ -1,3 +1,4 @@ +import datetime import pytest from django.contrib.auth.models import ContentType, Permission, User @@ -105,6 +106,8 @@ class APIFieldsTestCases: with django_db_blocker.unblock(): + random_str = datetime.datetime.now(tz=datetime.timezone.utc) + request.cls.organization = organization_one request.cls.different_organization = organization_two @@ -150,7 +153,7 @@ class APIFieldsTestCases: ) view_team = Team.objects.create( - team_name = 'cs_api_view_team', + team_name = 'cs_api_view_team' + str(random_str), organization = request.cls.organization, ) @@ -158,8 +161,7 @@ class APIFieldsTestCases: view_team.permissions.set([view_permissions]) - - request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password") + request.cls.view_user = User.objects.create_user(username="cafs_test_user_view" + str(random_str), password="password") team_user = TeamUsers.objects.create( team = view_team, diff --git a/app/conftest.py b/app/conftest.py index 1cf5a4e2..594a0513 100644 --- a/app/conftest.py +++ b/app/conftest.py @@ -1,3 +1,4 @@ +import datetime import pytest import sys @@ -485,8 +486,10 @@ def organization_one(django_db_blocker): with django_db_blocker.unblock(): + random_str = datetime.datetime.now(tz=datetime.timezone.utc) + item = Organization.objects.create( - name = 'org one' + name = 'org one from global' + str(random_str) ) yield item @@ -504,8 +507,10 @@ def organization_two(django_db_blocker): with django_db_blocker.unblock(): + random_str = datetime.datetime.now(tz=datetime.timezone.utc) + item = Organization.objects.create( - name = 'org two' + name = 'org two from global' + str(random_str) ) yield item diff --git a/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py b/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py index d48e5244..57d41726 100644 --- a/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py +++ b/app/core/tests/functional/ticket_comment_base/test_functional_ticket_comment_base_model.py @@ -39,6 +39,10 @@ class TicketCommentBaseModelTestCases( with django_db_blocker.unblock(): + for comment in ticket.ticketcommentbase_set.all(): + + comment.delete() + ticket.delete() diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py index 7380b87f..0515450f 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py @@ -1,3 +1,4 @@ +import datetime import pytest from django.contrib.auth.models import User @@ -154,6 +155,8 @@ class TicketCommentBaseModelTestCases( with django_db_blocker.unblock(): + random_str = datetime.datetime.now(tz=datetime.timezone.utc) + request.cls.organization = organization_one request.cls.different_organization = organization_two @@ -176,7 +179,7 @@ class TicketCommentBaseModelTestCases( request.cls.kwargs_create_item = kwargs_create_item - request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password") + request.cls.view_user = User.objects.create_user(username="ticket_comment_user_"+ str(random_str), password="password") comment_category = TicketCommentCategory.objects.create( organization = request.cls.organization, From 3196006badb7b52a58244ed62f8efb2f65903d24 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 23:31:08 +0930 Subject: [PATCH 113/175] feat(core): Add Action comments on ticket change ref: #754 #723 --- app/core/models/ticket_base.py | 285 ++++++++++++++++++++++++++++++++- 1 file changed, 278 insertions(+), 7 deletions(-) diff --git a/app/core/models/ticket_base.py b/app/core/models/ticket_base.py index 517b9992..d2b8405b 100644 --- a/app/core/models/ticket_base.py +++ b/app/core/models/ticket_base.py @@ -1,4 +1,5 @@ import datetime +import difflib from django.apps import apps from django.contrib.auth.models import User @@ -10,10 +11,11 @@ from access.fields import AutoCreatedField, AutoLastModifiedField from access.models.entity import Entity from access.models.tenancy import TenancyObject -from core import exceptions as centurion_exception +from core import exceptions as centurion_exceptions from core.classes.badge import Badge from core.lib.feature_not_used import FeatureNotUsed from core.lib.slash_commands import SlashCommands +from core.middleware.get_request import get_request from core.models.ticket.ticket_category import TicketCategory from core.models.ticket.ticket_enum_values import TicketValues @@ -26,6 +28,15 @@ class TicketBase( TenancyObject, ): + _after: dict + """History After + Data after save was called + """ + + _before: dict + """History Before + Data before save was called + """ save_model_history: bool = False @@ -561,15 +572,15 @@ class TicketBase( """Model Validation Raises: - centurion_exception.ValidationError: Milestone project does not + centurion_exceptions.ValidationError: Milestone project does not match the project assigned to the ticket. - centurion_exception.ValidationError: Tried to solve a ticket when + centurion_exceptions.ValidationError: Tried to solve a ticket when there are unresolved ticket comments. """ if self.opened_by is None: - raise centurion_exception.ValidationError( + raise centurion_exceptions.ValidationError( detail = { 'opened_by': 'This field is required.' }, @@ -581,7 +592,7 @@ class TicketBase( if self.milestone.project != self.project: - raise centurion_exception.ValidationError( + raise centurion_exceptions.ValidationError( detail = { 'milestone': f'Milestone is from project {self.milestone.project} when it should be from project {self.project}.' }, @@ -684,7 +695,7 @@ class TicketBase( and raise_exceptions ): - raise centurion_exception.ValidationError( + raise centurion_exceptions.ValidationError( detail = { 'status': 'You cant close this ticket.' }, @@ -711,7 +722,7 @@ class TicketBase( elif not comment.is_closed and raise_exceptions: - raise centurion_exception.ValidationError( + raise centurion_exceptions.ValidationError( detail = { 'status': 'You cant solve a ticket when there are un-resolved comments.' }, @@ -860,6 +871,253 @@ class TicketBase( return FeatureNotUsed + + def create_action_comment(self) -> None: + + from core.models.ticket_comment_action import TicketCommentAction + + request = get_request() + + excluded_fields: list = [ + 'created', + 'date_closed', + 'date_solved', + 'is_closed', + 'is_solved', + 'modified' + ] + changed_fields: list = [] + + for field, value in self._before.items(): + + if ( + self._before[field] != self._after[field] + and field not in excluded_fields + and field in self.fields + ): + + changed_fields = changed_fields + [ field ] + + + for field in changed_fields: + + comment_text: str = None + + if field == 'category_id': + + value = 'None' + + if self._before[field]: + + value = f"$ticket_category-{self._before[field]}" + + to_value = getattr(self.category, 'id', 'None') + + if to_value != 'None': + + to_value = f"$ticket_category-{getattr(self.category, 'id', 'None')}" + + + comment_text = f"changed category from {value} to {to_value}" + + elif field == 'impact': + + comment_text = f"changed {field} to {self.get_impact_display()}" + + elif field == 'urgency': + + comment_text = f"changed {field} to {self.get_urgency_display()}" + + elif field == 'priority': + + comment_text = f"changed {field} to {self.get_priority_display()}" + + + elif field == 'organization': + + comment_text = f"Ticket moved from $organization-{self._before[field]} to $organization-{self._after[field]}" + + elif field == 'parent_ticket_id': + + value = 'None' + + if self._before[field]: + + value = f"#{self._before[field]}" + + to_value = getattr(self.parent_ticket, 'id', 'None') + + if to_value != 'None': + + to_value = f"#{getattr(self.parent_ticket, 'id', 'None')}" + + comment_text = f"Parent ticket changed from {value} to {to_value}" + + elif field == 'status': + + comment_text = f"changed {field} to {self.get_status_display()}" + + elif field == 'title': + + comment_text = f"Title changed ~~{self._before[field]}~~ to **{self._after[field]}**" + + elif field == 'project_id': + + value = 'None' + + if self._before[field]: + + value = f"$project-{self._before[field]}" + + to_value = getattr(self.project, 'id', 'None') + + if to_value != 'None': + + to_value = f"$project-{getattr(self.project, 'id', 'None')}" + + + comment_text = f"changed project from {value} to {to_value}" + + elif field == 'milestone_id': + + value = 'None' + + if self._before[field]: + + value = f"$milestone-{self._before[field]}" + + to_value = getattr(self.milestone, 'id', 'None') + + if to_value != 'None': + + to_value = f"$milestone-{getattr(self.milestone, 'id', 'None')}" + + + comment_text = f"changed milestone from {value} to {to_value}" + + elif field == 'planned_start_date': + + to_value = self._after[field] + + if to_value: + + to_value = str(self._after[field].utcfromtimestamp(self._after[field].timestamp()))+ '+00:00' + + comment_text = f"changed Planned Start Date from _{self._before[field]}_ to **{to_value}**" + + elif field == 'planned_finish_date': + + to_value = self._after[field] + + if to_value: + + to_value = str(self._after[field].utcfromtimestamp(self._after[field].timestamp()))+ '+00:00' + + comment_text = f"changed Planned Finish Date from _{self._before[field]}_ to **{to_value}**" + + elif field == 'real_start_date': + + to_value = self._after[field] + + if to_value: + + to_value = str(self._after[field].utcfromtimestamp(self._after[field].timestamp()))+ '+00:00' + + comment_text = f"changed Real Start Date from _{self._before[field]}_ to **{to_value}**" + + to_value = self._after[field] + + if to_value: + + to_value = str(self._after[field].utcfromtimestamp(self._after[field].timestamp()))+ '+00:00' + + elif field == 'real_finish_date': + + to_value = self._after[field] + + if to_value: + + to_value = str(self._after[field].utcfromtimestamp(self._after[field].timestamp()))+ '+00:00' + + comment_text = f"changed Real Finish Date from _{self._before[field]}_ to **{to_value}**" + + + elif field == 'description': + + comment_text = ''.join( + str(x) for x in list( + difflib.unified_diff( + str(self._before[field] + '\n').splitlines(keepends=True), + str(self._after[field] + '\n').splitlines(keepends=True), + fromfile = 'before', + tofile = 'after', + n = 10000, + lineterm = '\n' + ) + ) + ) + '' + + comment_text = '
Changed the Description\n\n``` diff \n\n' + comment_text + '\n\n```\n\n
' + + + if ( + comment_text is None + and field != 'created' + and field != 'modified' + ): + + raise centurion_exceptions.APIError( + detail = f'Action comment for field {field} will not be created. please report this as a bug.', + code = 'no_action_comment' + ) + + elif comment_text: + + if request: + + if request.user.pk: + + comment_user = request.user + + else: + + comment_user = None + + else: + + comment_user = None + + comment = TicketCommentAction.objects.create( + organization = self.organization, + ticket = self, + comment_type = TicketCommentAction._meta.sub_model_type, + body = comment_text, + # user = user + ) + + # comment.save() + a = 'b' + + + + + + # return None + + + + # def create_action_comment(self, user): + + # from core.models.ticket_comment_action import TicketCommentAction + + # comment = TicketCommentAction.objects.create( + # organization = self.organization, + # ticket = self.ticket, + # body = body, + # # user = user + # ) + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): @@ -875,5 +1133,18 @@ class TicketBase( self.description = description + self._before = {} + + try: + self._before = self.__class__.objects.get(pk=self.pk).__dict__.copy() + except Exception: + pass + super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) + self._after = self.__dict__.copy() + + if self._before: + + self.create_action_comment() + From df82650931999c3339507e02c6f7f0e109538b4a Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 23:40:35 +0930 Subject: [PATCH 114/175] fix(core): Only take action on ticket comment if view exists ref: #754 #726 --- app/core/serializers/ticket_comment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/core/serializers/ticket_comment.py b/app/core/serializers/ticket_comment.py index 50708e3f..28487a11 100644 --- a/app/core/serializers/ticket_comment.py +++ b/app/core/serializers/ticket_comment.py @@ -177,7 +177,7 @@ class ModelSerializer( super().__init__(*args, **kwargs) - if kwargs['context']['view'].action in ['create', 'partial_update', 'update']: + if getattr(kwargs['context'].get('view'), 'action', '') in ['create', 'partial_update', 'update']: self.fields.fields['parent'].write_only = True self.fields.fields['ticket'].write_only = True @@ -212,7 +212,7 @@ class ModelSerializer( 'source', ] - self.Meta.read_only_fields = read_only_fields + self.Meta.read_only_fields = read_only_fields From e0c0c69d35f9186fed5fdb2ba38ebef0b6801f25 Mon Sep 17 00:00:00 2001 From: Jon Date: Tue, 13 May 2025 23:58:18 +0930 Subject: [PATCH 115/175] chore(test): Ensure comment cleanup during ticket tests ref: #754 #726 --- .../ticket_base/test_functional_ticket_base_serializer.py | 4 ++++ .../tests/unit/ticket_base/test_unit_ticket_base_model.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py index bde943d2..af655826 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py @@ -809,6 +809,10 @@ class TicketBaseSerializerTestCases: if serializer.instance: + for comment in serializer.instance.ticketcommentbase_set.all(): + + comment.delete() + serializer.instance.delete() diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py index 833038b4..9f2e9b27 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py @@ -313,6 +313,10 @@ class TicketBaseModelTestCases( request.cls.entity_user.delete() + for comment in parent_ticket.ticketcommentbase_set.all(): + + comment.delete() + parent_ticket.delete() project_milestone.delete() @@ -565,6 +569,10 @@ class TicketBaseModelTestCases( if ticket.pk is not None: + for comment in ticket.ticketcommentbase_set.all(): + + comment.delete() + ticket.delete() From 85afbbc01e77a710e306c6c1d4550d41302c1db2 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 00:21:55 +0930 Subject: [PATCH 116/175] chore(test): introduce further randomness to class fixtures so as not to create duplicate errors ref: #754 --- .../test_unit_ticket_comment_action_model.py | 4 +++- .../test_unit_ticket_comment_base_model.py | 14 ++++++++++---- .../test_unit_ticket_comment_solution_model.py | 4 +++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py index 6163f288..2bc0bb89 100644 --- a/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py +++ b/app/core/tests/unit/ticket_comment_action/test_unit_ticket_comment_action_model.py @@ -46,10 +46,12 @@ class TicketCommentActionModelTestCases( del valid_data['external_system'] del valid_data['external_ref'] - model.objects.create( + comment = model.objects.create( **valid_data ) + comment.delete() + assert spy.assert_called_once diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py index 0515450f..7c4cc9d2 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py @@ -183,19 +183,19 @@ class TicketCommentBaseModelTestCases( comment_category = TicketCommentCategory.objects.create( organization = request.cls.organization, - name = 'test cat comment' + name = 'test cat comment'+ str(random_str) ) ticket = TicketBase.objects.create( organization = request.cls.organization, - title = 'tester comment ticket', + title = 'tester comment ticket'+ str(random_str), description = 'aa', opened_by = request.cls.view_user, ) user = Person.objects.create( organization = request.cls.organization, - f_name = 'ip', + f_name = 'ip'+ str(random_str), l_name = 'funny' ) @@ -220,6 +220,10 @@ class TicketCommentBaseModelTestCases( comment_category.delete() + for comment in ticket.ticketcommentbase_set.all(): + + comment.delete() + ticket.delete() user.delete() @@ -477,10 +481,12 @@ class TicketCommentBaseModelTestCases( del valid_data['external_system'] del valid_data['external_ref'] - model.objects.create( + comment = model.objects.create( **valid_data ) + comment.delete() + assert spy.assert_called_once diff --git a/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py index 516688b0..ac175a2a 100644 --- a/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py +++ b/app/core/tests/unit/ticket_comment_solution/test_unit_ticket_comment_solution_model.py @@ -48,10 +48,12 @@ class TicketCommentSolutionModelTestCases( del valid_data['external_system'] del valid_data['external_ref'] - model.objects.create( + comment = model.objects.create( **valid_data ) + comment.delete() + assert spy.assert_called_once From f5be3b0f8e2bcca494c780b6230b9c88fff4bff1 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 03:41:00 +0930 Subject: [PATCH 117/175] feat(base): Add var `AUTH_USER_MODEL` to settings ref: #754 #704 #755 --- app/app/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/app/settings.py b/app/app/settings.py index 588d5015..59a3c1d4 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -20,7 +20,7 @@ import django.db.models.options as options options.DEFAULT_NAMES = (*options.DEFAULT_NAMES, 'sub_model_type', 'itam_sub_model_type') - +AUTH_USER_MODEL = 'auth.User' # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent From 6d92c484cd182b4ed56e4ee3bc1088f2ff8c8c24 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 03:45:40 +0930 Subject: [PATCH 118/175] refactor(access): Update all references to `User` to use `get_user_model()` ref: #754 #704 #755 --- app/access/admin.py | 5 ++++- app/access/middleware/request.py | 6 +++++- app/access/mixins/organization.py | 6 +++++- app/access/models/organization.py | 8 ++++++-- app/access/models/team_user.py | 6 +++++- app/access/models/tenancy.py | 5 ++--- .../functional/entity/test_functional_entity_viewset.py | 6 +++++- .../organization/test_organization_serializer.py | 4 +++- .../functional/organization/test_organization_viewset.py | 5 ++++- .../tests/functional/role/test_functional_role_viewset.py | 5 ++++- .../tests/functional/team/test_team_permission_viewset.py | 5 ++++- app/access/tests/functional/team/test_team_serializer.py | 5 ++++- .../team_user/test_team_user_permission_viewset.py | 5 ++++- .../functional/team_user/test_team_user_serializer.py | 5 ++++- app/access/tests/unit/entity/test_unit_entity_api_v2.py | 6 +++++- app/access/tests/unit/mixin/test_permission.py | 6 +++++- .../tests/unit/organization/test_organizaiton_api.py | 5 ++++- .../tests/unit/organization/test_organizaiton_api_v2.py | 5 ++++- .../unit/organization/test_organizaiton_permission.py | 7 +++++-- .../unit/organization/test_organizaiton_permission_api.py | 5 ++++- app/access/tests/unit/role/test_unit_role_api_v2.py | 6 +++++- app/access/tests/unit/team/test_team_api.py | 5 ++++- app/access/tests/unit/team/test_team_api_v2.py | 5 ++++- app/access/tests/unit/team/test_team_permission.py | 6 ++++-- app/access/tests/unit/team/test_team_permission_api.py | 5 ++++- app/access/tests/unit/team_user/test_team_user_api_v2.py | 5 ++++- .../tests/unit/team_user/test_team_user_permission.py | 6 ++++-- .../tests/unit/team_user/test_unit_team_user_model.py | 4 +++- 28 files changed, 118 insertions(+), 34 deletions(-) diff --git a/app/access/admin.py b/app/access/admin.py index f5031fe6..a519b93c 100644 --- a/app/access/admin.py +++ b/app/access/admin.py @@ -1,5 +1,6 @@ +import django from django.contrib import admin -from django.contrib.auth.models import Group, User +from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin @@ -7,6 +8,8 @@ from access.models.organization import Organization from access.models.team import Team from access.models.team_user import TeamUsers +User = django.contrib.auth.get_user_model() + admin.site.unregister(Group) class TeamInline(admin.TabularInline): diff --git a/app/access/middleware/request.py b/app/access/middleware/request.py index d6cefae3..460234d8 100644 --- a/app/access/middleware/request.py +++ b/app/access/middleware/request.py @@ -1,9 +1,11 @@ +import django + from django.contrib.auth.middleware import ( AuthenticationMiddleware, SimpleLazyObject, partial, ) -from django.contrib.auth.models import User, Group +from django.contrib.auth.models import Group from django.utils.deprecation import MiddlewareMixin @@ -13,6 +15,8 @@ from access.models.team import Team from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class RequestTenancy(MiddlewareMixin): """Access Middleware diff --git a/app/access/mixins/organization.py b/app/access/mixins/organization.py index 2d29d313..a90103a6 100644 --- a/app/access/mixins/organization.py +++ b/app/access/mixins/organization.py @@ -1,9 +1,13 @@ -from django.contrib.auth.models import User, Group +import django + +from django.contrib.auth.models import Group from django.db import models from access.models.organization import Organization from access.models.team import Team +User = django.contrib.auth.get_user_model() + class OrganizationMixin: diff --git a/app/access/models/organization.py b/app/access/models/organization.py index 8cb2b9df..b7561019 100644 --- a/app/access/models/organization.py +++ b/app/access/models/organization.py @@ -1,5 +1,7 @@ +import django + +from django.conf import settings from django.db import models -from django.contrib.auth.models import User from rest_framework.reverse import reverse @@ -11,6 +13,8 @@ from access.fields import ( from core.mixin.history_save import SaveHistory +User = django.contrib.auth.get_user_model() + class Organization(SaveHistory): @@ -44,7 +48,7 @@ class Organization(SaveHistory): ) manager = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank = False, help_text = 'Manager for this organization', null = True, diff --git a/app/access/models/team_user.py b/app/access/models/team_user.py index ae2c0dcd..d8aa0fdc 100644 --- a/app/access/models/team_user.py +++ b/app/access/models/team_user.py @@ -1,6 +1,8 @@ +import django + from django.conf import settings from django.db import models -from django.contrib.auth.models import User, Group +from django.contrib.auth.models import Group from rest_framework.reverse import reverse @@ -15,6 +17,8 @@ from access.models.team import Team from core.lib.feature_not_used import FeatureNotUsed from core.mixin.history_save import SaveHistory +User = django.contrib.auth.get_user_model() + class TeamUsers(SaveHistory): diff --git a/app/access/models/tenancy.py b/app/access/models/tenancy.py index 427d9a8e..a44dc2d8 100644 --- a/app/access/models/tenancy.py +++ b/app/access/models/tenancy.py @@ -1,11 +1,10 @@ +import django import logging + from django.db import models -# from django.contrib.auth.models import User, Group from rest_framework.reverse import reverse -# from .fields import * - from access.models.organization import Organization from core import exceptions as centurion_exceptions diff --git a/app/access/tests/functional/entity/test_functional_entity_viewset.py b/app/access/tests/functional/entity/test_functional_entity_viewset.py index 906fb292..7d1d4720 100644 --- a/app/access/tests/functional/entity/test_functional_entity_viewset.py +++ b/app/access/tests/functional/entity/test_functional_entity_viewset.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -11,6 +13,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from api.tests.abstract.api_permissions_viewset import APIPermissions from api.tests.abstract.api_serializer_viewset import SerializersTestCases +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/access/tests/functional/organization/test_organization_serializer.py b/app/access/tests/functional/organization/test_organization_serializer.py index 8eef1360..d5abfcd3 100644 --- a/app/access/tests/functional/organization/test_organization_serializer.py +++ b/app/access/tests/functional/organization/test_organization_serializer.py @@ -1,6 +1,6 @@ +import django import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -10,6 +10,8 @@ from access.serializers.organization import ( OrganizationModelSerializer ) +User = django.contrib.auth.get_user_model() + class OrganizationValidationAPI( diff --git a/app/access/tests/functional/organization/test_organization_viewset.py b/app/access/tests/functional/organization/test_organization_viewset.py index 13eb155f..75575d95 100644 --- a/app/access/tests/functional/organization/test_organization_viewset.py +++ b/app/access/tests/functional/organization/test_organization_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from api.tests.abstract.api_permissions_viewset import APIPermissions from api.tests.abstract.api_serializer_viewset import SerializersTestCases from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/access/tests/functional/role/test_functional_role_viewset.py b/app/access/tests/functional/role/test_functional_role_viewset.py index 6deb5f90..b2eafa8c 100644 --- a/app/access/tests/functional/role/test_functional_role_viewset.py +++ b/app/access/tests/functional/role/test_functional_role_viewset.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import Permission, User +import django +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import Client, TestCase @@ -15,6 +16,8 @@ from api.tests.abstract.api_serializer_viewset import SerializersTestCases from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/access/tests/functional/team/test_team_permission_viewset.py b/app/access/tests/functional/team/test_team_permission_viewset.py index 1a113aa7..ec001554 100644 --- a/app/access/tests/functional/team/test_team_permission_viewset.py +++ b/app/access/tests/functional/team/test_team_permission_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import Client, TestCase @@ -19,6 +20,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from api.tests.abstract.api_permissions_viewset import APIPermissions from api.tests.abstract.api_serializer_viewset import SerializersTestCases +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/access/tests/functional/team/test_team_serializer.py b/app/access/tests/functional/team/test_team_serializer.py index 8b910682..0135ced1 100644 --- a/app/access/tests/functional/team/test_team_serializer.py +++ b/app/access/tests/functional/team/test_team_serializer.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -17,6 +18,8 @@ from access.serializers.teams import ( from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/access/tests/functional/team_user/test_team_user_permission_viewset.py b/app/access/tests/functional/team_user/test_team_user_permission_viewset.py index a921a6c2..8855b9ca 100644 --- a/app/access/tests/functional/team_user/test_team_user_permission_viewset.py +++ b/app/access/tests/functional/team_user/test_team_user_permission_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import Client, TestCase @@ -18,6 +19,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from api.tests.abstract.api_permissions_viewset import APIPermissions from api.tests.abstract.api_serializer_viewset import SerializersTestCases +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/access/tests/functional/team_user/test_team_user_serializer.py b/app/access/tests/functional/team_user/test_team_user_serializer.py index 7b703b8e..694ee624 100644 --- a/app/access/tests/functional/team_user/test_team_user_serializer.py +++ b/app/access/tests/functional/team_user/test_team_user_serializer.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,8 @@ from access.serializers.team_user import ( TeamUserModelSerializer ) +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/access/tests/unit/entity/test_unit_entity_api_v2.py b/app/access/tests/unit/entity/test_unit_entity_api_v2.py index e3f72810..c0d1310c 100644 --- a/app/access/tests/unit/entity/test_unit_entity_api_v2.py +++ b/app/access/tests/unit/entity/test_unit_entity_api_v2.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -12,6 +14,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APITenancyObject +User = django.contrib.auth.get_user_model() + class APITestCases( diff --git a/app/access/tests/unit/mixin/test_permission.py b/app/access/tests/unit/mixin/test_permission.py index f7699e3a..ce04fddf 100644 --- a/app/access/tests/unit/mixin/test_permission.py +++ b/app/access/tests/unit/mixin/test_permission.py @@ -1,7 +1,9 @@ +import django + import pytest from unittest.mock import Mock, patch -from django.contrib.auth.models import User, Permission, AnonymousUser +from django.contrib.auth.models import Permission, AnonymousUser from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -23,6 +25,8 @@ from core.models.manufacturer import Manufacturer from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MyMockView( diff --git a/app/access/tests/unit/organization/test_organizaiton_api.py b/app/access/tests/unit/organization/test_organizaiton_api.py index 1d23d717..8e6b0f47 100644 --- a/app/access/tests/unit/organization/test_organizaiton_api.py +++ b/app/access/tests/unit/organization/test_organizaiton_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -13,6 +14,8 @@ from access.models.organization import Organization from access.models.team import Team from access.models.team_user import TeamUsers +User = django.contrib.auth.get_user_model() + diff --git a/app/access/tests/unit/organization/test_organizaiton_api_v2.py b/app/access/tests/unit/organization/test_organizaiton_api_v2.py index 92f74776..73b9f36f 100644 --- a/app/access/tests/unit/organization/test_organizaiton_api_v2.py +++ b/app/access/tests/unit/organization/test_organizaiton_api_v2.py @@ -1,8 +1,9 @@ +import django import pytest import unittest from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +17,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APICommonFields +User = django.contrib.auth.get_user_model() + class OrganizationAPI( TestCase, diff --git a/app/access/tests/unit/organization/test_organizaiton_permission.py b/app/access/tests/unit/organization/test_organizaiton_permission.py index d5153897..e90b71f8 100644 --- a/app/access/tests/unit/organization/test_organizaiton_permission.py +++ b/app/access/tests/unit/organization/test_organizaiton_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -16,6 +17,8 @@ from access.tests.abstract.model_permissions_organization_manager import Organiz from app.tests.abstract.model_permissions import ModelPermissionsView, ModelPermissionsChange +User = django.contrib.auth.get_user_model() + class OrganizationPermissions( TestCase, diff --git a/app/access/tests/unit/organization/test_organizaiton_permission_api.py b/app/access/tests/unit/organization/test_organizaiton_permission_api.py index 53ee58db..46f2f416 100644 --- a/app/access/tests/unit/organization/test_organizaiton_permission_api.py +++ b/app/access/tests/unit/organization/test_organizaiton_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -13,6 +14,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_permissions import APIPermissionChange, APIPermissionView +User = django.contrib.auth.get_user_model() + class OrganizationPermissionsAPI(TestCase, APIPermissionChange, APIPermissionView): diff --git a/app/access/tests/unit/role/test_unit_role_api_v2.py b/app/access/tests/unit/role/test_unit_role_api_v2.py index ac7bde47..5faaeee0 100644 --- a/app/access/tests/unit/role/test_unit_role_api_v2.py +++ b/app/access/tests/unit/role/test_unit_role_api_v2.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -12,6 +14,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APITenancyObject +User = django.contrib.auth.get_user_model() + class APITestCases( diff --git a/app/access/tests/unit/team/test_team_api.py b/app/access/tests/unit/team/test_team_api.py index d30875ee..01ccab6b 100644 --- a/app/access/tests/unit/team/test_team_api.py +++ b/app/access/tests/unit/team/test_team_api.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from access.models.team_user import TeamUsers # from api.tests.abstract.api_permissions import APIPermissions +User = django.contrib.auth.get_user_model() + class TeamAPI(TestCase): diff --git a/app/access/tests/unit/team/test_team_api_v2.py b/app/access/tests/unit/team/test_team_api_v2.py index 97116f48..e57290f2 100644 --- a/app/access/tests/unit/team/test_team_api_v2.py +++ b/app/access/tests/unit/team/test_team_api_v2.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -13,6 +14,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APITenancyObject +User = django.contrib.auth.get_user_model() + class TeamAPI( diff --git a/app/access/tests/unit/team/test_team_permission.py b/app/access/tests/unit/team/test_team_permission.py index 27e17112..d1dfe840 100644 --- a/app/access/tests/unit/team/test_team_permission.py +++ b/app/access/tests/unit/team/test_team_permission.py @@ -1,6 +1,6 @@ -# from django.conf import settings +import django from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -16,6 +16,8 @@ from access.tests.abstract.model_permissions_organization_manager import Organiz from app.tests.abstract.model_permissions import ModelPermissions +User = django.contrib.auth.get_user_model() + class TeamPermissions( diff --git a/app/access/tests/unit/team/test_team_permission_api.py b/app/access/tests/unit/team/test_team_permission_api.py index 15b16e6b..17d32d8e 100644 --- a/app/access/tests/unit/team/test_team_permission_api.py +++ b/app/access/tests/unit/team/test_team_permission_api.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_permissions import APIPermissions +User = django.contrib.auth.get_user_model() + class TeamPermissionsAPI(TestCase, APIPermissions): diff --git a/app/access/tests/unit/team_user/test_team_user_api_v2.py b/app/access/tests/unit/team_user/test_team_user_api_v2.py index 545a4d79..20fc8f57 100644 --- a/app/access/tests/unit/team_user/test_team_user_api_v2.py +++ b/app/access/tests/unit/team_user/test_team_user_api_v2.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -13,6 +14,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APICommonFields +User = django.contrib.auth.get_user_model() + class TeamUserAPI( diff --git a/app/access/tests/unit/team_user/test_team_user_permission.py b/app/access/tests/unit/team_user/test_team_user_permission.py index e98410eb..7478722b 100644 --- a/app/access/tests/unit/team_user/test_team_user_permission.py +++ b/app/access/tests/unit/team_user/test_team_user_permission.py @@ -1,6 +1,6 @@ -# from django.conf import settings +import django from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +17,8 @@ from access.tests.abstract.model_permissions_organization_manager import Organiz from app.tests.abstract.model_permissions import ModelPermissionsAdd, ModelPermissionsChange, ModelPermissionsDelete +User = django.contrib.auth.get_user_model() + class TeamUserPermissions( diff --git a/app/access/tests/unit/team_user/test_unit_team_user_model.py b/app/access/tests/unit/team_user/test_unit_team_user_model.py index 87a07c2f..4ea8011a 100644 --- a/app/access/tests/unit/team_user/test_unit_team_user_model.py +++ b/app/access/tests/unit/team_user/test_unit_team_user_model.py @@ -1,5 +1,5 @@ +import django from django.test import TestCase -from django.contrib.auth.models import User from access.models.organization import Organization from access.models.team import Team @@ -7,6 +7,8 @@ from access.models.team_user import TeamUsers from app.tests.unit.test_unit_models import NonTenancyObjectInheritedCases +User = django.contrib.auth.get_user_model() + class TeamUsersModel( From cce885d961356afc92aedf3b84f9eb62d1a4b7d5 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 03:49:23 +0930 Subject: [PATCH 119/175] refactor(accounting): Update all references to `User` to use `get_user_model()` ref: #754 #755 --- .../asset_base/test_functional_asset_base_metadata.py | 5 ++++- .../asset_base/test_functional_asset_base_serializer.py | 3 ++- .../asset_base/test_functional_asset_base_viewset.py | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py index aea0b4b3..9eab4637 100644 --- a/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import Permission, User +import django +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -10,6 +11,8 @@ from accounting.models.asset_base import AssetBase from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional +User = django.contrib.auth.get_user_model() + class MetadataTestCases( diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py index cf4fcc79..7fad24e9 100644 --- a/app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_serializer.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import User +User = django.contrib.auth.get_user_model() diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py index 9d752602..496b88b8 100644 --- a/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import Permission, User +import django +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -10,6 +11,8 @@ from accounting.models.asset_base import AssetBase from api.tests.abstract.api_serializer_viewset import SerializersTestCases +User = django.contrib.auth.get_user_model() + class ViewSetBase: From 817c8d63e16386bfc837e0829c492b667766040c Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 03:55:29 +0930 Subject: [PATCH 120/175] refactor(api): Update all references to `User` to use `get_user_model()` ref: #754 #755 --- .../auth_token/test_functional_auth_token_viewset.py | 5 ++++- .../tests/functional/test_functional_api_permissions.py | 5 ++++- app/api/tests/unit/test_index_viewset.py | 5 ++++- app/api/tests/unit/test_navigation_menu.py | 7 ++++++- app/api/tests/unit/test_unit_api_fields.py | 5 ++++- app/api/tests/unit/test_unit_common_viewset.py | 5 ++++- app/api/tests/unit/token/test_token.py | 6 +++++- app/api/tests/unit/token/test_unit_auth_token_api_v2.py | 5 ++++- 8 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py b/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py index da71d00c..6bfa4267 100644 --- a/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py +++ b/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import Permission, User +import django +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -23,6 +24,8 @@ from api.tests.abstract.test_metadata_functional import ( MetadataAttributesFunctionalBase, ) +User = django.contrib.auth.get_user_model() + diff --git a/app/api/tests/functional/test_functional_api_permissions.py b/app/api/tests/functional/test_functional_api_permissions.py index fcc54abb..d11b55c6 100644 --- a/app/api/tests/functional/test_functional_api_permissions.py +++ b/app/api/tests/functional/test_functional_api_permissions.py @@ -1,12 +1,15 @@ +import django import pytest -from django.contrib.auth.models import ContentType, Permission, User +from django.contrib.auth.models import ContentType, Permission from django.shortcuts import reverse from django.test import Client from access.models.team import Team from access.models.team_user import TeamUsers +User = django.contrib.auth.get_user_model() + class APIPermissionAddInheritedCases: diff --git a/app/api/tests/unit/test_index_viewset.py b/app/api/tests/unit/test_index_viewset.py index 0d39fd08..08de51ac 100644 --- a/app/api/tests/unit/test_index_viewset.py +++ b/app/api/tests/unit/test_index_viewset.py @@ -1,4 +1,4 @@ -from django.contrib.auth.models import User +import django from django.shortcuts import reverse from django.test import Client, TestCase @@ -8,6 +8,9 @@ from api.tests.unit.test_unit_common_viewset import IndexViewsetInheritedCases from api.viewsets.index import Index +User = django.contrib.auth.get_user_model() + + class HomeViewset( TestCase, diff --git a/app/api/tests/unit/test_navigation_menu.py b/app/api/tests/unit/test_navigation_menu.py index 11ede50f..97816578 100644 --- a/app/api/tests/unit/test_navigation_menu.py +++ b/app/api/tests/unit/test_navigation_menu.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import Permission, User +import django +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import Client, TestCase @@ -8,6 +9,10 @@ from access.models.team_user import TeamUsers from api.react_ui_metadata import ReactUIMetadata +User = django.contrib.auth.get_user_model() + + + class MockRequst: user = None diff --git a/app/api/tests/unit/test_unit_api_fields.py b/app/api/tests/unit/test_unit_api_fields.py index c9dc6a3a..ce230543 100644 --- a/app/api/tests/unit/test_unit_api_fields.py +++ b/app/api/tests/unit/test_unit_api_fields.py @@ -1,7 +1,8 @@ import datetime +import django import pytest -from django.contrib.auth.models import ContentType, Permission, User +from django.contrib.auth.models import ContentType, Permission from django.shortcuts import reverse from django.test import Client @@ -12,6 +13,8 @@ from access.models.team_user import TeamUsers from app.tests.common import DoesNotExist +User = django.contrib.auth.get_user_model() + class APIFieldsTestCases: diff --git a/app/api/tests/unit/test_unit_common_viewset.py b/app/api/tests/unit/test_unit_common_viewset.py index bcce810d..addb2429 100644 --- a/app/api/tests/unit/test_unit_common_viewset.py +++ b/app/api/tests/unit/test_unit_common_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import ContentType, Permission, User +from django.contrib.auth.models import ContentType, Permission from django.db import models from django.test import TestCase @@ -47,6 +48,8 @@ from api.viewsets.common import ( from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MockRequest: diff --git a/app/api/tests/unit/token/test_token.py b/app/api/tests/unit/token/test_token.py index cf4cd100..0d8d9212 100644 --- a/app/api/tests/unit/token/test_token.py +++ b/app/api/tests/unit/token/test_token.py @@ -1,9 +1,9 @@ +import django import hashlib import json from datetime import datetime, timedelta -from django.contrib.auth.models import User from django.shortcuts import reverse from django.test import TestCase, Client @@ -13,6 +13,10 @@ from api.models.tokens import AuthToken from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + + + class APIAuthToken(TestCase): diff --git a/app/api/tests/unit/token/test_unit_auth_token_api_v2.py b/app/api/tests/unit/token/test_unit_auth_token_api_v2.py index b3ac7925..a19be6be 100644 --- a/app/api/tests/unit/token/test_unit_auth_token_api_v2.py +++ b/app/api/tests/unit/token/test_unit_auth_token_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -15,6 +16,8 @@ from api.tests.abstract.api_fields import APIModelFields from core.models.manufacturer import Manufacturer +User = django.contrib.auth.get_user_model() + class API( From c00aa6fa73f4cb93ca0ea76c920291d4e2973a8f Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 03:59:15 +0930 Subject: [PATCH 121/175] refactor(app): Update all references to `User` to use `get_user_model()` ref: #754 #755 --- app/app/serializers/user.py | 4 +++- app/app/tests/abstract/mock_view.py | 4 +++- .../content_type/test_content_type_permission_viewset.py | 5 ++++- .../permission/test_permissions_permission_viewset.py | 5 ++++- .../tests/functional/user/test_user_permission_viewset.py | 4 +++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/app/serializers/user.py b/app/app/serializers/user.py index f4671cf1..f1ebc16a 100644 --- a/app/app/serializers/user.py +++ b/app/app/serializers/user.py @@ -1,7 +1,9 @@ -from django.contrib.auth.models import User +import django from rest_framework import serializers +User = django.contrib.auth.get_user_model() + class UserBaseSerializer(serializers.ModelSerializer): diff --git a/app/app/tests/abstract/mock_view.py b/app/app/tests/abstract/mock_view.py index d979a52f..77f8880c 100644 --- a/app/app/tests/abstract/mock_view.py +++ b/app/app/tests/abstract/mock_view.py @@ -1,11 +1,13 @@ -from django.contrib.auth.models import User +import django from access.middleware.request import Tenancy from access.models.organization import Organization from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/app/tests/functional/content_type/test_content_type_permission_viewset.py b/app/app/tests/functional/content_type/test_content_type_permission_viewset.py index 250a24be..9a59adea 100644 --- a/app/app/tests/functional/content_type/test_content_type_permission_viewset.py +++ b/app/app/tests/functional/content_type/test_content_type_permission_viewset.py @@ -1,11 +1,14 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import User, ContentType +from django.contrib.auth.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase +User = django.contrib.auth.get_user_model() + class ContentTypePermissionsAPI(TestCase): diff --git a/app/app/tests/functional/permission/test_permissions_permission_viewset.py b/app/app/tests/functional/permission/test_permissions_permission_viewset.py index 6fbbe990..83cf5852 100644 --- a/app/app/tests/functional/permission/test_permissions_permission_viewset.py +++ b/app/app/tests/functional/permission/test_permissions_permission_viewset.py @@ -1,11 +1,14 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import User, Permission +from django.contrib.auth.models import Permission from django.shortcuts import reverse from django.test import Client, TestCase +User = django.contrib.auth.get_user_model() + class PermissionPermissionsAPI(TestCase): diff --git a/app/app/tests/functional/user/test_user_permission_viewset.py b/app/app/tests/functional/user/test_user_permission_viewset.py index 0ee146a9..d968e017 100644 --- a/app/app/tests/functional/user/test_user_permission_viewset.py +++ b/app/app/tests/functional/user/test_user_permission_viewset.py @@ -1,11 +1,13 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import User from django.shortcuts import reverse from django.test import Client, TestCase +User = django.contrib.auth.get_user_model() + class UserPermissionsAPI(TestCase): From 09ab52b9718904a35bcb8f81dd629f0b9ea9dad6 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 04:04:51 +0930 Subject: [PATCH 122/175] refactor(assistance): Update all references to `User` to use `get_user_model()` ref: #754 #755 --- app/assistance/models/knowledge_base.py | 5 ++++- .../test_functional_knowledge_base_history.py | 5 ++++- .../knowledge_base/test_knowledge_base_serializer.py | 4 +++- .../knowledge_base/test_knowledge_base_viewset.py | 5 ++++- .../test_knowledge_base_category_serializer.py | 6 ++++-- .../test_knowledge_base_category_viewset.py | 4 +++- .../test_model_knowledge_base_article_serializer.py | 4 +++- .../test_model_knowledge_base_article_viewset.py | 5 ++++- .../unit/knowledge_base/test_knowledge_base_api_v2.py | 5 ++++- .../unit/knowledge_base/test_knowledge_base_permission.py | 7 +++++-- .../test_knowledge_base_category_api_v2.py | 6 +++++- .../test_knowledge_base_category_permission.py | 7 +++++-- .../test_model_knowledge_base_article_api_v2.py | 5 ++++- 13 files changed, 52 insertions(+), 16 deletions(-) diff --git a/app/assistance/models/knowledge_base.py b/app/assistance/models/knowledge_base.py index cbef0928..046e4af8 100644 --- a/app/assistance/models/knowledge_base.py +++ b/app/assistance/models/knowledge_base.py @@ -1,4 +1,4 @@ -from django.contrib.auth.models import User +import django from django.db import models from django.forms import ValidationError @@ -6,6 +6,9 @@ from access.fields import * from access.models.team import Team from access.models.tenancy import TenancyObject +User = django.contrib.auth.get_user_model() + + class KnowledgeBaseCategory(TenancyObject): diff --git a/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_history.py b/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_history.py index 443aa81c..2843cec8 100644 --- a/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_history.py +++ b/app/assistance/tests/functional/knowledge_base/test_functional_knowledge_base_history.py @@ -1,10 +1,13 @@ -from django.contrib.auth.models import User +import django + from django.test import TestCase from assistance.models.knowledge_base_history import KnowledgeBase, KnowledgeBaseHistory from core.tests.abstract.test_functional_history import HistoryEntriesCommon +User = django.contrib.auth.get_user_model() + class History( diff --git a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py index d75c3fae..8d97cbec 100644 --- a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py +++ b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py @@ -1,7 +1,7 @@ +import django import json import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -14,6 +14,8 @@ from app.tests.abstract.mock_view import MockView from assistance.models.knowledge_base import KnowledgeBase from assistance.serializers.knowledge_base import KnowledgeBaseModelSerializer +User = django.contrib.auth.get_user_model() + class KnowledgeBaseValidationAPI( diff --git a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py index 0ffc42c5..202fab61 100644 --- a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py +++ b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -20,6 +21,8 @@ from assistance.models.knowledge_base import KnowledgeBase from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + diff --git a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py index 957c5f82..c21d6b4c 100644 --- a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py +++ b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py @@ -1,7 +1,7 @@ +import django import json import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -9,10 +9,12 @@ from rest_framework.exceptions import ValidationError from access.models.organization import Organization from access.models.team import Team -from app.tests.abstract.mock_view import MockView, User +from app.tests.abstract.mock_view import MockView from assistance.serializers.knowledge_base_category import KnowledgeBaseCategory, KnowledgeBaseCategoryModelSerializer +User = django.contrib.auth.get_user_model() + class KnowledgeBaseCategoryValidationAPI( diff --git a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py index 925fcbd4..b1b1869f 100644 --- a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py +++ b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,7 @@ from assistance.models.knowledge_base import KnowledgeBaseCategory from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() diff --git a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py index 85711af9..d653a9c0 100644 --- a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py +++ b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py @@ -1,7 +1,7 @@ +import django import json import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -14,6 +14,8 @@ from assistance.serializers.model_knowledge_base_article import ModelKnowledgeBa from itam.models.device import Device +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py index 50a25a6c..682c82f2 100644 --- a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py +++ b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import Client, TestCase @@ -27,6 +28,8 @@ from assistance.models.model_knowledge_base_article import KnowledgeBase, ModelK from itam.models.device import Device +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py index 35509ae4..b195c786 100644 --- a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py +++ b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -15,6 +16,8 @@ from api.tests.abstract.api_fields import APITenancyObject from assistance.models.knowledge_base import KnowledgeBase, KnowledgeBaseCategory +User = django.contrib.auth.get_user_model() + class KnowledgeBaseAPI( TestCase, diff --git a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py index 8da1fb40..310ad732 100644 --- a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py +++ b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,8 @@ from app.tests.abstract.model_permissions import ModelPermissions from assistance.models.knowledge_base import KnowledgeBase +User = django.contrib.auth.get_user_model() + class KnowledgeBasePermissions(TestCase, ModelPermissions): diff --git a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py index ef6ef009..86de81ee 100644 --- a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py +++ b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -15,6 +16,9 @@ from api.tests.abstract.api_fields import APITenancyObject from assistance.models.knowledge_base import KnowledgeBase, KnowledgeBaseCategory +User = django.contrib.auth.get_user_model() + + class KnowledgeBaseCategoryAPI( TestCase, diff --git a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py index c269678b..4b8e6e30 100644 --- a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py +++ b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,8 @@ from app.tests.abstract.model_permissions import ModelPermissions from assistance.models.knowledge_base import KnowledgeBaseCategory +User = django.contrib.auth.get_user_model() + class KnowledgeBasePermissions(TestCase, ModelPermissions): diff --git a/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py b/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py index c9236c32..6eab5b65 100644 --- a/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py +++ b/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,8 @@ from assistance.models.knowledge_base import KnowledgeBaseCategory from itam.models.device import Device +User = django.contrib.auth.get_user_model() + class ModelKnowledgeBaseArticleAPI( From 55cf10d289247cfc42b98fe9e619b850b0c1df92 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 04:10:29 +0930 Subject: [PATCH 123/175] refactor(config_management): Update all references to `User` to use `get_user_model()` ref: #754 #755 --- .../functional/config_groups/test_config_groups_viewset.py | 5 ++++- .../test_config_groups_software_viewset.py | 5 ++++- .../tests/unit/config_groups/test_config_groups_api.py | 5 ++++- .../tests/unit/config_groups/test_config_groups_api_v2.py | 5 ++++- .../unit/config_groups/test_config_groups_permission.py | 7 +++++-- .../test_config_groups_software_api_v2.py | 6 ++++-- .../test_config_groups_software_permission.py | 7 +++++-- 7 files changed, 30 insertions(+), 10 deletions(-) diff --git a/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py b/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py index f3129c8d..548bf5bc 100644 --- a/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py +++ b/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -20,6 +21,8 @@ from config_management.models.groups import ConfigGroups from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + diff --git a/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py b/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py index 701933ce..0294931d 100644 --- a/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py +++ b/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -18,6 +19,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from config_management.models.groups import ConfigGroups, ConfigGroupSoftware, Software, SoftwareVersion +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/config_management/tests/unit/config_groups/test_config_groups_api.py b/app/config_management/tests/unit/config_groups/test_config_groups_api.py index aeee214c..4036cdf0 100644 --- a/app/config_management/tests/unit/config_groups/test_config_groups_api.py +++ b/app/config_management/tests/unit/config_groups/test_config_groups_api.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -12,6 +13,8 @@ from access.models.team_user import TeamUsers from config_management.models.groups import ConfigGroups +User = django.contrib.auth.get_user_model() + @pytest.mark.django_db diff --git a/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py b/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py index 2518945a..de629ccd 100644 --- a/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py +++ b/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_fields import APITenancyObject from config_management.models.groups import ConfigGroups +User = django.contrib.auth.get_user_model() + class ConfigGroupsAPI( diff --git a/app/config_management/tests/unit/config_groups/test_config_groups_permission.py b/app/config_management/tests/unit/config_groups/test_config_groups_permission.py index 64df781b..b46645b7 100644 --- a/app/config_management/tests/unit/config_groups/test_config_groups_permission.py +++ b/app/config_management/tests/unit/config_groups/test_config_groups_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,8 @@ from app.tests.abstract.model_permissions import ModelPermissions from config_management.models.groups import ConfigGroups +User = django.contrib.auth.get_user_model() + class ConfigGroupPermissions(TestCase, ModelPermissions): diff --git a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py index 0bfc707e..7ccec211 100644 --- a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py +++ b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py @@ -1,7 +1,7 @@ +import django import pytest -# import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +16,8 @@ from api.tests.abstract.api_fields import APITenancyObject from config_management.models.groups import DeviceSoftware, ConfigGroups, ConfigGroupSoftware, Software, SoftwareVersion +User = django.contrib.auth.get_user_model() + class ConfigGroupsAPI( diff --git a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py index 6fc6cde3..33dca38b 100644 --- a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py +++ b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -20,6 +21,8 @@ from config_management.models.groups import ConfigGroups, ConfigGroupSoftware from itam.models.device import DeviceSoftware from itam.models.software import Software +User = django.contrib.auth.get_user_model() + class ConfigGroupSoftwarePermissions(TestCase, ModelPermissionsAdd, ModelPermissionsChange, ModelPermissionsDelete): From 2ea42d10e686f95241dce1ed3c45124b2bd91d77 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 04:37:38 +0930 Subject: [PATCH 124/175] refactor(core): Update all references to `User` to use `get_user_model()` ref: #754 #755 --- app/core/models/history.py | 9 +++++++-- app/core/models/manufacturer.py | 5 ++++- app/core/models/model_history.py | 9 +++++++-- app/core/models/model_notes.py | 11 ++++++++--- app/core/models/notes.py | 10 +++++++--- app/core/models/ticket/ticket.py | 11 +++++++---- app/core/models/ticket/ticket_comment.py | 10 +++++++--- app/core/models/ticket_base.py | 7 +++++-- app/core/tests/abstract/history_permissions.py | 4 +++- app/core/tests/abstract/model_notes_api_fields.py | 6 +++++- .../tests/abstract/test_functional_notes_viewset.py | 6 +++++- app/core/tests/abstract/test_item_ticket_api_v2.py | 6 +++++- app/core/tests/abstract/test_ticket_api_v2.py | 7 ++++++- app/core/tests/abstract/test_ticket_serializer.py | 5 ++++- app/core/tests/abstract/test_ticket_viewset.py | 6 +++++- .../tests/abstract/test_unit_model_history_api_v2.py | 5 ++++- .../abstract/test_unit_model_notes_serializer.py | 5 ++++- .../manufacturer/test_manufacturer_viewset.py | 5 ++++- .../related_ticket/test_related_ticket_serializer.py | 4 +++- .../related_ticket/test_related_ticket_viewset.py | 5 ++++- .../slash_commands/test_slash_command_related.py | 5 ++++- .../test_history/test_functional_history_viewset.py | 6 +++++- .../test_task_result/test_task_result_viewset.py | 6 +++++- .../test_functional_ticket_base_metadata.py | 6 +++++- .../test_functional_ticket_base_serializer.py | 5 +++-- .../test_functional_ticket_base_viewset.py | 6 +++++- .../ticket_category/test_ticket_category_viewset.py | 5 ++++- .../ticket_comment/test_ticket_comment_serializer.py | 5 ++++- .../ticket_comment/test_ticket_comment_viewset.py | 6 +++++- .../test_ticket_comment_category_viewset.py | 5 ++++- .../test_ticket_linked_item_serializer.py | 5 ++++- .../test_ticket_linked_item_viewset.py | 6 +++++- .../unit/manufacturer/test_manufacturer_api_v2.py | 5 ++++- .../unit/manufacturer/test_manufacturer_permission.py | 8 ++++++-- .../unit/model_notes/test_unit_model_notes_model.py | 5 ++++- .../unit/related_ticket/test_related_ticket_api_v2.py | 6 ++++-- .../unit/test_history/test_unit_history_model.py | 6 +++++- .../tests/unit/test_task_result/test_task_result.py | 5 ++++- .../unit/test_task_result/test_task_result_api_v2.py | 5 ++++- .../unit/ticket_base/test_unit_ticket_base_model.py | 6 ++++-- .../ticket_category/test_ticket_category_api_v2.py | 5 ++++- .../test_ticket_category_permission.py | 8 ++++++-- .../test_ticket_category_permission_api.py | 6 +++++- .../unit/ticket_comment/test_ticket_comment_api_v2.py | 5 ++++- .../ticket_comment/test_ticket_comment_permission.py | 4 +++- .../test_ticket_comment_permission_api.py | 6 +++++- .../ticket_comment/test_unit_ticket_comment_model.py | 4 +++- .../test_unit_ticket_comment_base_api_fields.py | 5 ++++- .../test_unit_ticket_comment_base_model.py | 4 +++- .../test_unit_ticket_comment_base_viewset.py | 6 +++++- .../test_ticket_comment_category_api_v2.py | 5 ++++- .../test_ticket_comment_category_permission.py | 7 +++++-- .../test_ticket_comment_category_permission_api.py | 6 +++++- .../test_ticket_depreciated_permission.py | 5 ++++- .../test_ticket_depreciated_permission_api.py | 6 +++++- .../test_unit_ticket_depreciated_model.py | 5 ++++- .../ticket_permission/field_based_permissions.py | 6 +++++- .../test_ticket_linked_item_api_v2.py | 6 ++++-- .../test_ticket_linked_item_permission.py | 6 +++++- .../test_unit_ticket_linked_item_model.py | 5 ++++- 60 files changed, 279 insertions(+), 79 deletions(-) diff --git a/app/core/models/history.py b/app/core/models/history.py index 3f77899e..fd2a0a9f 100644 --- a/app/core/models/history.py +++ b/app/core/models/history.py @@ -1,8 +1,13 @@ -from django.contrib.auth.models import User +import django + +from django.conf import settings from django.db import models from access.fields import * +User = django.contrib.auth.get_user_model() + + class HistoryCommonFields(models.Model): @@ -70,7 +75,7 @@ class History(HistoryCommonFields): user = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= False, help_text = 'User whom performed the action this history relates to', null = True, diff --git a/app/core/models/manufacturer.py b/app/core/models/manufacturer.py index 72ce9750..5b793edd 100644 --- a/app/core/models/manufacturer.py +++ b/app/core/models/manufacturer.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User +import django + from django.db import models from access.fields import * @@ -8,6 +9,8 @@ from core.mixin.history_save import SaveHistory from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ManufacturerCommonFields(models.Model): diff --git a/app/core/models/model_history.py b/app/core/models/model_history.py index 4388620f..4367b204 100644 --- a/app/core/models/model_history.py +++ b/app/core/models/model_history.py @@ -1,4 +1,7 @@ -from django.contrib.auth.models import ContentType, User +import django + +from django.conf import settings +from django.contrib.auth.models import ContentType from django.db import models from rest_framework.reverse import reverse @@ -9,6 +12,8 @@ from access.models.tenancy import TenancyObject from core.lib.feature_not_used import FeatureNotUsed +User = django.contrib.auth.get_user_model() + class ModelHistory( @@ -77,7 +82,7 @@ class ModelHistory( ) user = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= False, help_text = 'User whom performed the action this history relates to', null = True, diff --git a/app/core/models/model_notes.py b/app/core/models/model_notes.py index 17343df5..c04cb6fd 100644 --- a/app/core/models/model_notes.py +++ b/app/core/models/model_notes.py @@ -1,4 +1,7 @@ -from django.contrib.auth.models import User, ContentType +import django + +from django.conf import settings +from django.contrib.auth.models import ContentType from django.db import models from rest_framework.reverse import reverse @@ -16,6 +19,8 @@ from itam.models.operating_system import OperatingSystem from itim.models.services import Service +User = django.contrib.auth.get_user_model() + class ModelNotes(TenancyObject): @@ -63,7 +68,7 @@ class ModelNotes(TenancyObject): created_by = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank = True, help_text = 'User whom added the Note', null = False, @@ -73,7 +78,7 @@ class ModelNotes(TenancyObject): ) modified_by = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= True, default = None, help_text = 'User whom modified the note', diff --git a/app/core/models/notes.py b/app/core/models/notes.py index 08cb5b7a..2b0e5015 100644 --- a/app/core/models/notes.py +++ b/app/core/models/notes.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import User +import django + +from django.conf import settings from django.db import models from rest_framework.reverse import reverse @@ -14,6 +16,8 @@ from itam.models.operating_system import OperatingSystem from itim.models.services import Service +User = django.contrib.auth.get_user_model() + class NotesCommonFields(TenancyObject, models.Model): @@ -65,7 +69,7 @@ class Notes(NotesCommonFields): usercreated = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= True, default = None, help_text = 'User whom added Note', @@ -76,7 +80,7 @@ class Notes(NotesCommonFields): ) usermodified = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= True, default = None, help_text = 'User whom modified the note', diff --git a/app/core/models/ticket/ticket.py b/app/core/models/ticket/ticket.py index 124fa9bc..db5f6c36 100644 --- a/app/core/models/ticket/ticket.py +++ b/app/core/models/ticket/ticket.py @@ -1,6 +1,7 @@ import difflib +import django -from django.contrib.auth.models import User +from django.conf import settings from django.db import models from django.db.models import Q, signals, Sum from django.forms import ValidationError @@ -21,6 +22,8 @@ from core.models.ticket.ticket_category import TicketCategory, KnowledgeBase from project_management.models.project_milestone import Project, ProjectMilestone +User = django.contrib.auth.get_user_model() + class TicketCommonFields(models.Model): @@ -470,7 +473,7 @@ class Ticket( opened_by = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= False, help_text = 'Who is the ticket for', null = False, @@ -481,7 +484,7 @@ class Ticket( subscribed_users = models.ManyToManyField( - User, + settings.AUTH_USER_MODEL, blank= True, help_text = 'Subscribe a User(s) to the ticket to receive updates', related_name = 'subscribed_users', @@ -500,7 +503,7 @@ class Ticket( ) assigned_users = models.ManyToManyField( - User, + settings.AUTH_USER_MODEL, blank= True, help_text = 'Assign the ticket to a User(s)', related_name = 'assigned_users', diff --git a/app/core/models/ticket/ticket_comment.py b/app/core/models/ticket/ticket_comment.py index 7ec0feed..ec66e82e 100644 --- a/app/core/models/ticket/ticket_comment.py +++ b/app/core/models/ticket/ticket_comment.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import User +import django + +from django.conf import settings from django.db import models from django.forms import ValidationError @@ -14,6 +16,8 @@ from core.lib.slash_commands import SlashCommands from .ticket import Ticket from .ticket_comment_category import TicketCommentCategory +User = django.contrib.auth.get_user_model() + class TicketComment( @@ -241,7 +245,7 @@ class TicketComment( ) responsible_user = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= True, default = None, help_text = 'User whom is responsible for the completion of comment', @@ -263,7 +267,7 @@ class TicketComment( ) user = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= False, help_text = 'Who made the comment', null = True, diff --git a/app/core/models/ticket_base.py b/app/core/models/ticket_base.py index d2b8405b..4f092e17 100644 --- a/app/core/models/ticket_base.py +++ b/app/core/models/ticket_base.py @@ -1,8 +1,9 @@ import datetime import difflib +import django from django.apps import apps -from django.contrib.auth.models import User +from django.conf import settings from django.db import models from rest_framework.reverse import reverse @@ -21,6 +22,8 @@ from core.models.ticket.ticket_enum_values import TicketValues from project_management.models.project_milestone import Project, ProjectMilestone +User = django.contrib.auth.get_user_model() + class TicketBase( @@ -444,7 +447,7 @@ class TicketBase( opened_by = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank = True, help_text = 'Who is the ticket for', null = True, diff --git a/app/core/tests/abstract/history_permissions.py b/app/core/tests/abstract/history_permissions.py index ae43a7e6..8e5fef49 100644 --- a/app/core/tests/abstract/history_permissions.py +++ b/app/core/tests/abstract/history_permissions.py @@ -1,7 +1,7 @@ +import django import pytest import unittest -from django.contrib.auth.models import User from django.shortcuts import reverse from django.test import TestCase, Client @@ -9,6 +9,8 @@ from core.models.history import History from itam.models.device import Device +User = django.contrib.auth.get_user_model() + class HistoryPermissions: diff --git a/app/core/tests/abstract/model_notes_api_fields.py b/app/core/tests/abstract/model_notes_api_fields.py index 39109e67..da9adbc5 100644 --- a/app/core/tests/abstract/model_notes_api_fields.py +++ b/app/core/tests/abstract/model_notes_api_fields.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -11,6 +13,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APITenancyObject +User = django.contrib.auth.get_user_model() + class ModelNotesNotesAPIFields( diff --git a/app/core/tests/abstract/test_functional_notes_viewset.py b/app/core/tests/abstract/test_functional_notes_viewset.py index 61fc2d1d..57570151 100644 --- a/app/core/tests/abstract/test_functional_notes_viewset.py +++ b/app/core/tests/abstract/test_functional_notes_viewset.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import AnonymousUser, Permission, User +import django + +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from access.models.organization import Organization @@ -12,6 +14,8 @@ from api.tests.abstract.api_serializer_viewset import SerializersTestCases from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ModelNotesViewSetBase: diff --git a/app/core/tests/abstract/test_item_ticket_api_v2.py b/app/core/tests/abstract/test_item_ticket_api_v2.py index 987b0e4c..03214f5e 100644 --- a/app/core/tests/abstract/test_item_ticket_api_v2.py +++ b/app/core/tests/abstract/test_item_ticket_api_v2.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import User, Permission +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase @@ -13,6 +15,8 @@ from api.tests.abstract.api_fields import APITenancyObject from core.models.ticket.ticket_linked_items import Ticket, TicketLinkedItem +User = django.contrib.auth.get_user_model() + class ItemTicketAPI( diff --git a/app/core/tests/abstract/test_ticket_api_v2.py b/app/core/tests/abstract/test_ticket_api_v2.py index 8d4a0156..6fe6a4f3 100644 --- a/app/core/tests/abstract/test_ticket_api_v2.py +++ b/app/core/tests/abstract/test_ticket_api_v2.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +18,9 @@ from core.models.ticket.ticket_category import TicketCategory from project_management.models.projects import Project from project_management.models.project_milestone import ProjectMilestone +User = django.contrib.auth.get_user_model() + + class TicketAPI( APITenancyObject diff --git a/app/core/tests/abstract/test_ticket_serializer.py b/app/core/tests/abstract/test_ticket_serializer.py index c1cd88d2..0ef800db 100644 --- a/app/core/tests/abstract/test_ticket_serializer.py +++ b/app/core/tests/abstract/test_ticket_serializer.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -20,6 +21,8 @@ from project_management.models.project_milestone import ProjectMilestone from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/core/tests/abstract/test_ticket_viewset.py b/app/core/tests/abstract/test_ticket_viewset.py index 98770c3d..bd6610e2 100644 --- a/app/core/tests/abstract/test_ticket_viewset.py +++ b/app/core/tests/abstract/test_ticket_viewset.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client @@ -16,6 +18,8 @@ from project_management.models.projects import Project from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class TicketViewSetBase: diff --git a/app/core/tests/abstract/test_unit_model_history_api_v2.py b/app/core/tests/abstract/test_unit_model_history_api_v2.py index 7cb2204b..bb2d3977 100644 --- a/app/core/tests/abstract/test_unit_model_history_api_v2.py +++ b/app/core/tests/abstract/test_unit_model_history_api_v2.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User +import django + from django.shortcuts import reverse from django.test import Client @@ -10,6 +11,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APITenancyObject +User = django.contrib.auth.get_user_model() + class BaseModelHistoryAPI( diff --git a/app/core/tests/abstract/test_unit_model_notes_serializer.py b/app/core/tests/abstract/test_unit_model_notes_serializer.py index 771e34ea..0f8175d8 100644 --- a/app/core/tests/abstract/test_unit_model_notes_serializer.py +++ b/app/core/tests/abstract/test_unit_model_notes_serializer.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User +import django + from django.contrib.contenttypes.models import ContentType from rest_framework.exceptions import ValidationError @@ -7,6 +8,8 @@ from access.models.organization import Organization from app.tests.abstract.mock_view import MockView +User = django.contrib.auth.get_user_model() + class ModelNotesSerializerTestCases: diff --git a/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py b/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py index 1016e670..3f36928e 100644 --- a/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py +++ b/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -20,6 +21,8 @@ from core.models.manufacturer import Manufacturer from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + diff --git a/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py b/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py index 4805f21b..a56dab90 100644 --- a/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py +++ b/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py @@ -1,6 +1,6 @@ +import django import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -15,6 +15,8 @@ from core.serializers.ticket_related import ( RelatedTicketModelSerializer, ) +User = django.contrib.auth.get_user_model() + class RelatedTicketsValidationAPI( diff --git a/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py b/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py index 514ef63d..6e59bf8c 100644 --- a/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py +++ b/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -23,6 +24,8 @@ from api.tests.abstract.test_metadata_functional import ( from core.models.ticket.ticket import Ticket, RelatedTickets +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/core/tests/functional/slash_commands/test_slash_command_related.py b/app/core/tests/functional/slash_commands/test_slash_command_related.py index edba7c29..176a2059 100644 --- a/app/core/tests/functional/slash_commands/test_slash_command_related.py +++ b/app/core/tests/functional/slash_commands/test_slash_command_related.py @@ -1,8 +1,8 @@ +import django import pytest import re import unittest -from django.contrib.auth.models import User from django.test import TestCase from access.models.organization import Organization @@ -18,6 +18,9 @@ from core.models.ticket_comment_base import TicketBase, TicketCommentBase from itam.models.device import Device from itam.models.software import Software +User = django.contrib.auth.get_user_model() + + class SlashCommandsCommon: """Slash Command Test cases diff --git a/app/core/tests/functional/test_history/test_functional_history_viewset.py b/app/core/tests/functional/test_history/test_functional_history_viewset.py index e593818f..d25ececf 100644 --- a/app/core/tests/functional/test_history/test_functional_history_viewset.py +++ b/app/core/tests/functional/test_history/test_functional_history_viewset.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import ContentType, Permission, User +import django + +from django.contrib.auth.models import ContentType, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -20,6 +22,8 @@ from itam.models.device_history import DeviceHistory from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/core/tests/functional/test_task_result/test_task_result_viewset.py b/app/core/tests/functional/test_task_result/test_task_result_viewset.py index 9e812a18..4c99c8b1 100644 --- a/app/core/tests/functional/test_task_result/test_task_result_viewset.py +++ b/app/core/tests/functional/test_task_result/test_task_result_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -21,6 +22,9 @@ from api.tests.abstract.api_permissions_viewset import ( from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + + class TaskResultPermissionsAPI( TestCase, diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py index 912994c6..de66dcc7 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -10,6 +12,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from core.models.ticket_base import TicketBase +User = django.contrib.auth.get_user_model() + class MetadataTestCases( diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py index af655826..6cc1625d 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_serializer.py @@ -1,7 +1,6 @@ +import django import pytest -from django.contrib.auth.models import User - from rest_framework.exceptions import ( ValidationError ) @@ -16,6 +15,8 @@ from project_management.models.project_milestone import ( ProjectMilestone, ) +User = django.contrib.auth.get_user_model() + class TicketBaseSerializerTestCases: diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py index c5448ba8..5ce752a8 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -10,6 +12,8 @@ from api.tests.abstract.api_serializer_viewset import SerializersTestCases from core.models.ticket_base import TicketBase +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py b/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py index 90239897..148f1923 100644 --- a/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py +++ b/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -20,6 +21,8 @@ from core.models.ticket.ticket_category import TicketCategory from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + diff --git a/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py b/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py index 5649a0f2..b66f75c8 100644 --- a/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py +++ b/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py @@ -1,7 +1,7 @@ +import django import pytest from django.test import TestCase -from django.contrib.auth.models import User from rest_framework.exceptions import ValidationError @@ -23,6 +23,9 @@ from core.serializers.ticket_comment_depreciated import ( from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + + class MockView: diff --git a/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py b/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py index 4eefa47f..facad4f3 100644 --- a/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py +++ b/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -14,6 +16,8 @@ from core.models.ticket.ticket_comment import Ticket, TicketComment from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py b/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py index abfe572b..4c3c4f7c 100644 --- a/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py +++ b/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -20,6 +21,8 @@ from core.models.ticket.ticket_comment_category import TicketCommentCategory from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + diff --git a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py index 2773bb1e..5c0f77af 100644 --- a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py +++ b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py @@ -1,7 +1,7 @@ +import django import pytest from django.test import TestCase -from django.contrib.auth.models import User from rest_framework.exceptions import ValidationError @@ -11,6 +11,9 @@ from core.serializers.ticket_linked_item import Ticket, TicketLinkedItem, Ticket from itam.models.device import Device +User = django.contrib.auth.get_user_model() + + class TicketLinkedItemValidationAPI( TestCase, diff --git a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py index 6d22a64e..4478cb9c 100644 --- a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py +++ b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -23,6 +25,8 @@ from core.models.ticket.ticket_linked_items import Ticket, TicketLinkedItem from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py b/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py index 8f8b66f4..afdf1f99 100644 --- a/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py +++ b/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_fields import APITenancyObject from core.models.manufacturer import Manufacturer +User = django.contrib.auth.get_user_model() + class ManufacturerAPI( diff --git a/app/core/tests/unit/manufacturer/test_manufacturer_permission.py b/app/core/tests/unit/manufacturer/test_manufacturer_permission.py index 74839082..cbe2784a 100644 --- a/app/core/tests/unit/manufacturer/test_manufacturer_permission.py +++ b/app/core/tests/unit/manufacturer/test_manufacturer_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from core.models.manufacturer import Manufacturer +User = django.contrib.auth.get_user_model() + + class ManufacturerPermissions(TestCase, ModelPermissions): diff --git a/app/core/tests/unit/model_notes/test_unit_model_notes_model.py b/app/core/tests/unit/model_notes/test_unit_model_notes_model.py index c75798d6..8543aa16 100644 --- a/app/core/tests/unit/model_notes/test_unit_model_notes_model.py +++ b/app/core/tests/unit/model_notes/test_unit_model_notes_model.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User +import django + from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -8,6 +9,8 @@ from app.tests.unit.test_unit_models import TenancyObjectInheritedCases from core.models.model_notes import ModelNotes +User = django.contrib.auth.get_user_model() + class ModelNotesTestCases( diff --git a/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py b/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py index 241c9064..09aa54fc 100644 --- a/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py +++ b/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py @@ -1,6 +1,6 @@ -# import pytest +import django -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +17,8 @@ from core.models.ticket.ticket import Ticket, RelatedTickets from itam.models.device import Device +User = django.contrib.auth.get_user_model() + class RelatedTicketsLinkedItemAPI( diff --git a/app/core/tests/unit/test_history/test_unit_history_model.py b/app/core/tests/unit/test_history/test_unit_history_model.py index 1b694de0..2c5b2aca 100644 --- a/app/core/tests/unit/test_history/test_unit_history_model.py +++ b/app/core/tests/unit/test_history/test_unit_history_model.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import User, ContentType +import django + +from django.contrib.auth.models import ContentType from django.test import TestCase from access.models.organization import Organization @@ -8,6 +10,8 @@ from app.tests.unit.test_unit_models import TenancyObjectInheritedCases from core.models.manufacturer_history import Manufacturer from core.models.model_history import ModelHistory +User = django.contrib.auth.get_user_model() + class ModelHistoryTestCases( diff --git a/app/core/tests/unit/test_task_result/test_task_result.py b/app/core/tests/unit/test_task_result/test_task_result.py index 34ff6fc0..07806dc6 100644 --- a/app/core/tests/unit/test_task_result/test_task_result.py +++ b/app/core/tests/unit/test_task_result/test_task_result.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase @@ -17,6 +18,8 @@ from app.tests.abstract.model_permissions import ModelPermissionsView from django_celery_results.models import TaskResult +User = django.contrib.auth.get_user_model() + class TaskResultPermissions(TestCase, ModelPermissionsView): diff --git a/app/core/tests/unit/test_task_result/test_task_result_api_v2.py b/app/core/tests/unit/test_task_result/test_task_result_api_v2.py index 2877acaa..716817b3 100644 --- a/app/core/tests/unit/test_task_result/test_task_result_api_v2.py +++ b/app/core/tests/unit/test_task_result/test_task_result_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +17,8 @@ from access.models.team_user import TeamUsers from api.tests.abstract.api_fields import APICommonFields +User = django.contrib.auth.get_user_model() + class CeleryTaskResultAPI( diff --git a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py index 9f2e9b27..83b02ed7 100644 --- a/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py +++ b/app/core/tests/unit/ticket_base/test_unit_ticket_base_model.py @@ -1,7 +1,7 @@ -import pytest import datetime +import django +import pytest -from django.contrib.auth.models import User from django.db.models.query import QuerySet from django.db import models from django.test import TestCase @@ -22,6 +22,8 @@ from core.models.ticket_comment_base import TicketCommentBase from project_management.models.project_milestone import Project, ProjectMilestone +User = django.contrib.auth.get_user_model() + class TicketBaseModelTestCases( diff --git a/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py b/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py index 84c80c3e..48c17215 100644 --- a/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py +++ b/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_fields import APITenancyObject from core.models.ticket.ticket_category import TicketCategory +User = django.contrib.auth.get_user_model() + class TicketCategoryAPI( diff --git a/app/core/tests/unit/ticket_category/test_ticket_category_permission.py b/app/core/tests/unit/ticket_category/test_ticket_category_permission.py index 6e241bac..d1ffe0ee 100644 --- a/app/core/tests/unit/ticket_category/test_ticket_category_permission.py +++ b/app/core/tests/unit/ticket_category/test_ticket_category_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from core.models.ticket.ticket_category import TicketCategory +User = django.contrib.auth.get_user_model() + + class TicketCategoryPermissions(TestCase, ModelPermissions): diff --git a/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py b/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py index 9ac8639f..f08f6f97 100644 --- a/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py +++ b/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,9 @@ from api.tests.abstract.api_permissions import APIPermissions from core.models.ticket.ticket_category import TicketCategory +User = django.contrib.auth.get_user_model() + + class TicketCategoryPermissionsAPI(TestCase, APIPermissions): diff --git a/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py b/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py index e6b50ab6..1e1840ef 100644 --- a/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py +++ b/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +17,8 @@ from api.tests.abstract.api_fields import APITenancyObject from core.models.ticket.ticket import Ticket from core.models.ticket.ticket_comment import TicketComment, TicketCommentCategory +User = django.contrib.auth.get_user_model() + class TicketCommentAPI( diff --git a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py index 49ffb81b..8febfe90 100644 --- a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py +++ b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py @@ -1,7 +1,8 @@ +import django import re from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -21,6 +22,7 @@ from project_management.models.projects import Project from core.models.ticket.ticket import Ticket, RelatedTickets from core.models.ticket.ticket_comment import TicketComment +User = django.contrib.auth.get_user_model() diff --git a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py index 9e953330..f4629561 100644 --- a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py +++ b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +17,9 @@ from api.tests.abstract.api_permissions import APIPermissions, APIPermissionAdd, from core.models.ticket.ticket import Ticket from core.models.ticket.ticket_comment import TicketComment +User = django.contrib.auth.get_user_model() + + class TicketCommentPermissionsAPI( # APIPermissions diff --git a/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py b/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py index 21e3691f..988c2dc2 100644 --- a/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py +++ b/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py @@ -1,6 +1,6 @@ +import django import pytest -from django.contrib.auth.models import User from django.test import TestCase from access.models.organization import Organization @@ -11,6 +11,8 @@ from app.tests.unit.test_unit_models import ( from core.models.ticket.ticket_comment import Ticket, TicketComment +User = django.contrib.auth.get_user_model() + class TicketCommentModel( TenancyObjectInheritedCases, diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py index e14d54db..3f9ffab1 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_api_fields.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import ContentType, Permission, User +from django.contrib.auth.models import ContentType, Permission from django.shortcuts import reverse from rest_framework.relations import Hyperlink @@ -18,6 +19,8 @@ from core.models.ticket_comment_base import ( TicketCommentCategory ) +User = django.contrib.auth.get_user_model() + class TicketCommentBaseAPITestCases( diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py index 7c4cc9d2..06f0e53c 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_model.py @@ -1,7 +1,7 @@ import datetime +import django import pytest -from django.contrib.auth.models import User from django.db import models from rest_framework.exceptions import ValidationError @@ -14,6 +14,8 @@ from app.tests.unit.test_unit_models import ( from core.models.ticket_comment_base import TicketBase, TicketCommentBase, TicketCommentCategory +User = django.contrib.auth.get_user_model() + class TicketCommentBaseModelTestCases( diff --git a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py index 3ee30e00..ac0c29a6 100644 --- a/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py +++ b/app/core/tests/unit/ticket_comment_base/test_unit_ticket_comment_base_viewset.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User +import django + from django.test import Client, TestCase from rest_framework.reverse import reverse @@ -12,6 +13,9 @@ from core.viewsets.ticket_comment import ( ViewSet ) +User = django.contrib.auth.get_user_model() + + class TicketCommentBaseViewsetTestCases( SubModelViewSetInheritedCases, diff --git a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py index 70f1a177..7bab6eac 100644 --- a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py +++ b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_fields import APITenancyObject from core.models.ticket.ticket_comment_category import TicketCommentCategory +User = django.contrib.auth.get_user_model() + class TicketCategoryAPI( diff --git a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py index 102a5bd7..7df72b65 100644 --- a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py +++ b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py @@ -1,6 +1,6 @@ -# from django.conf import settings +import django from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +17,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from core.models.ticket.ticket_comment_category import TicketCommentCategory +User = django.contrib.auth.get_user_model() + + class TicketCommentCategoryPermissions(TestCase, ModelPermissions): diff --git a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py index d8f09306..4093e9ce 100644 --- a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py +++ b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,9 @@ from api.tests.abstract.api_permissions import APIPermissions from core.models.ticket.ticket_comment_category import TicketCommentCategory +User = django.contrib.auth.get_user_model() + + class TicketCommentCategoryPermissionsAPI(TestCase, APIPermissions): diff --git a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py index 6dbbb141..326a5958 100644 --- a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py +++ b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py @@ -1,9 +1,10 @@ +import django import re from datetime import datetime from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -28,6 +29,8 @@ from core.tests.unit.ticket_depreciated.ticket_permission.field_based_permission from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class SetUp: diff --git a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py index 6317cd85..d19585a1 100644 --- a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py +++ b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,9 @@ from api.tests.abstract.api_permissions import APIPermissions from core.models.ticket.ticket import Ticket +User = django.contrib.auth.get_user_model() + + class TicketPermissionsAPI(APIPermissions): diff --git a/app/core/tests/unit/ticket_depreciated/test_unit_ticket_depreciated_model.py b/app/core/tests/unit/ticket_depreciated/test_unit_ticket_depreciated_model.py index 1eb920ff..e7e1bbf8 100644 --- a/app/core/tests/unit/ticket_depreciated/test_unit_ticket_depreciated_model.py +++ b/app/core/tests/unit/ticket_depreciated/test_unit_ticket_depreciated_model.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User +import django + from django.test import TestCase from app.tests.unit.test_unit_models import ( @@ -7,6 +8,8 @@ from app.tests.unit.test_unit_models import ( from core.models.ticket.ticket import Ticket +User = django.contrib.auth.get_user_model() + class TicketModel( TenancyObjectInheritedCases, diff --git a/app/core/tests/unit/ticket_depreciated/ticket_permission/field_based_permissions.py b/app/core/tests/unit/ticket_depreciated/ticket_permission/field_based_permissions.py index e4da9731..444b7f7c 100644 --- a/app/core/tests/unit/ticket_depreciated/ticket_permission/field_based_permissions.py +++ b/app/core/tests/unit/ticket_depreciated/ticket_permission/field_based_permissions.py @@ -1,7 +1,8 @@ +import django import pytest from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, User +from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.shortcuts import reverse @@ -9,6 +10,9 @@ from django.test import Client from core.models.ticket.ticket import Ticket +User = django.contrib.auth.get_user_model() + + class TicketFieldPermissionsAddUser: diff --git a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py index f677512a..ccd5e6f0 100644 --- a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py +++ b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py @@ -1,6 +1,6 @@ -# import pytest +import django -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +17,8 @@ from core.models.ticket.ticket_linked_items import Ticket, TicketLinkedItem from itam.models.device import Device +User = django.contrib.auth.get_user_model() + class TicketLinkedItemAPI( diff --git a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py index da330bfe..df547a34 100644 --- a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py +++ b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py @@ -1,7 +1,8 @@ +import django import re from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -27,6 +28,9 @@ from itam.models.device import Device from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + + class TicketLinkedItemPermissions( ModelPermissionsAdd, diff --git a/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py b/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py index f6e11ef4..8d6f56dd 100644 --- a/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py +++ b/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User +import django + from django.test import TestCase from access.models.organization import Organization @@ -16,6 +17,8 @@ from itam.models.software import Software from itim.models.clusters import Cluster from itim.models.services import Service +User = django.contrib.auth.get_user_model() + class TicketLinkedItemBase: From 1b749e9f1a4994d108a0f25e5cae42b0050c0f6b Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 18:46:33 +0930 Subject: [PATCH 125/175] refactor(devops): Update all references to `User` to use `get_user_model()` ref: #756 #755 --- .../feature_flag/test_functional_feature_flag_viewset.py | 4 +++- ...st_functional_software_enable_feature_flag_serializer.py | 4 +++- .../test_functional_software_enable_feature_flag_viewset.py | 4 +++- .../unit/feature_flag/test_unit_feature_flag_api_v2.py | 6 +++++- .../test_unit_software_enable_feature_flag_api_v2.py | 6 +++++- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py b/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py index dfbd1e14..a0c46782 100644 --- a/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py +++ b/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py @@ -1,6 +1,7 @@ +import django + from django.contrib.auth.models import ( Permission, - User, ) from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -20,6 +21,7 @@ from itam.models.software import Software from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() diff --git a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py index 44f5d5a0..a4f92687 100644 --- a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py +++ b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py @@ -1,6 +1,6 @@ +import django import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -13,6 +13,8 @@ from devops.serializers.software_enable_feature_flag import SoftwareEnableFeatur from itam.models.software import Software +User = django.contrib.auth.get_user_model() + class ValidationAPI( diff --git a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py index 9dfa968c..cd8154c5 100644 --- a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py +++ b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py @@ -1,6 +1,7 @@ +import django + from django.contrib.auth.models import ( Permission, - User, ) from django.contrib.contenttypes.models import ContentType from django.test import Client, TestCase @@ -22,6 +23,7 @@ from itam.models.software import Software from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() diff --git a/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py b/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py index 0456df42..19ba8071 100644 --- a/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py +++ b/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -15,6 +17,8 @@ from devops.models.feature_flag import FeatureFlag from itam.models.software import Software +User = django.contrib.auth.get_user_model() + class API( diff --git a/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py b/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py index 13ae0289..60b50371 100644 --- a/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py +++ b/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py @@ -1,4 +1,6 @@ -from django.contrib.auth.models import Permission, User +import django + +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -15,6 +17,8 @@ from devops.models.software_enable_feature_flag import SoftwareEnableFeatureFlag from itam.models.software import Software +User = django.contrib.auth.get_user_model() + class API( From de9937606eb74b1e5cdfcff3d180e064db8f08cd Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 19:07:25 +0930 Subject: [PATCH 126/175] refactor(itam): Update all references to `User` to use `get_user_model()` ref: #756 #755 --- app/itam/models/device_models.py | 3 ++- app/itam/tests/functional/device/test_device_viewset.py | 5 ++++- .../functional/device_model/test_device_model_viewset.py | 5 ++++- .../test_device_operating_system_serializer.py | 5 ++++- .../test_device_operating_system_viewset.py | 5 ++++- .../device_software/test_device_software_serializer.py | 4 +++- .../device_software/test_device_software_viewset.py | 5 ++++- .../functional/device_type/test_device_type_viewset.py | 4 +++- .../test_operating_system_installs_viewset.py | 5 ++++- .../test_software_installs_serializer.py | 4 +++- .../test_software_installs_viewset.py | 5 ++++- .../tests/functional/inventory/test_inventory_viewset.py | 5 ++++- .../operating_system/test_operating_system_viewset.py | 4 +++- .../test_operating_system_version_viewset.py | 4 +++- .../tests/functional/software/test_software_viewset.py | 5 ++++- .../software_category/test_software_category_viewset.py | 5 ++++- .../software_version/test_software_version_viewset.py | 5 ++++- app/itam/tests/unit/device/test_device_api.py | 6 +++++- app/itam/tests/unit/device/test_device_api_v2.py | 5 ++++- app/itam/tests/unit/device/test_device_permission.py | 8 +++++--- app/itam/tests/unit/device/test_device_permission_api.py | 6 +++++- .../tests/unit/device_model/test_device_model_api_v2.py | 5 ++++- .../unit/device_model/test_device_model_permission.py | 9 ++++++--- .../test_device_operating_system_api_v2.py | 5 ++++- .../unit/device_software/test_device_software_api_v2.py | 5 ++++- .../tests/unit/device_type/test_device_type_api_v2.py | 5 ++++- .../unit/device_type/test_device_type_permission.py | 8 ++++++-- .../test_operating_system_installs_api_v2.py | 5 ++++- .../test_software_installs_api_v2.py | 5 ++++- app/itam/tests/unit/inventory/test_api_inventory.py | 5 ++++- .../unit/inventory/test_inventory_permission_api.py | 7 ++++++- .../operating_system/test_operating_system_api_v2.py | 5 ++++- .../operating_system/test_operating_system_permission.py | 7 +++++-- .../test_operating_system_version_api_v2.py | 5 ++++- app/itam/tests/unit/software/test_software_api.py | 5 ++++- app/itam/tests/unit/software/test_software_api_v2.py | 5 ++++- app/itam/tests/unit/software/test_software_permission.py | 8 ++++++-- .../tests/unit/software/test_software_permission_api.py | 6 +++++- .../software_category/test_software_category_api_v2.py | 5 ++++- .../test_software_category_permission.py | 8 ++++++-- .../software_version/test_software_version_api_v2.py | 5 ++++- 41 files changed, 172 insertions(+), 49 deletions(-) diff --git a/app/itam/models/device_models.py b/app/itam/models/device_models.py index 8fce87b3..24383f76 100644 --- a/app/itam/models/device_models.py +++ b/app/itam/models/device_models.py @@ -1,4 +1,3 @@ -from django.contrib.auth.models import User from django.db import models from itam.models.device_common import DeviceCommonFieldsName @@ -10,6 +9,8 @@ from core.models.manufacturer import Manufacturer from settings.models.app_settings import AppSettings + + class DeviceModel(DeviceCommonFieldsName, SaveHistory): diff --git a/app/itam/tests/functional/device/test_device_viewset.py b/app/itam/tests/functional/device/test_device_viewset.py index 5ea59ddf..4722a555 100644 --- a/app/itam/tests/functional/device/test_device_viewset.py +++ b/app/itam/tests/functional/device/test_device_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from itam.models.device import Device from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/device_model/test_device_model_viewset.py b/app/itam/tests/functional/device_model/test_device_model_viewset.py index f4fe45fc..d5eb308f 100644 --- a/app/itam/tests/functional/device_model/test_device_model_viewset.py +++ b/app/itam/tests/functional/device_model/test_device_model_viewset.py @@ -1,6 +1,8 @@ +import django + import pytest -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +18,7 @@ from itam.models.device import DeviceModel from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() diff --git a/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py b/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py index 9e337291..2a9ec334 100644 --- a/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py +++ b/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py @@ -1,6 +1,7 @@ +import django + import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -13,6 +14,8 @@ from itam.models.operating_system import OperatingSystem, OperatingSystemVersion from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py b/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py index ba8e509f..e592580c 100644 --- a/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py +++ b/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -15,6 +16,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from itam.serializers.device_operating_system import Device, DeviceOperatingSystem, DeviceOperatingSystemModelSerializer from itam.models.operating_system import OperatingSystem, OperatingSystemVersion +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/device_software/test_device_software_serializer.py b/app/itam/tests/functional/device_software/test_device_software_serializer.py index f2d3dcd5..bb379f37 100644 --- a/app/itam/tests/functional/device_software/test_device_software_serializer.py +++ b/app/itam/tests/functional/device_software/test_device_software_serializer.py @@ -1,6 +1,6 @@ +import django import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -13,6 +13,8 @@ from itam.models.software import Software, SoftwareCategory, SoftwareVersion from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/itam/tests/functional/device_software/test_device_software_viewset.py b/app/itam/tests/functional/device_software/test_device_software_viewset.py index 093f917c..f63c2cf4 100644 --- a/app/itam/tests/functional/device_software/test_device_software_viewset.py +++ b/app/itam/tests/functional/device_software/test_device_software_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_serializer_viewset import SerializersTestCases from itam.models.device import Device, DeviceSoftware from itam.models.software import Software +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/device_type/test_device_type_viewset.py b/app/itam/tests/functional/device_type/test_device_type_viewset.py index 72e2e43c..724e74e9 100644 --- a/app/itam/tests/functional/device_type/test_device_type_viewset.py +++ b/app/itam/tests/functional/device_type/test_device_type_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,7 @@ from itam.models.device import DeviceType from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() diff --git a/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py b/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py index 29d4aa64..bf0e4c62 100644 --- a/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py +++ b/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -15,6 +16,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from itam.serializers.device_operating_system import Device, DeviceOperatingSystem, DeviceOperatingSystemModelSerializer from itam.models.operating_system import OperatingSystem, OperatingSystemVersion +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py index 59bfbed3..49236e9a 100644 --- a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py +++ b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py @@ -1,6 +1,6 @@ +import django import pytest -from django.contrib.auth.models import User from django.test import TestCase from rest_framework.exceptions import ValidationError @@ -13,6 +13,8 @@ from itam.models.software import Software, SoftwareCategory, SoftwareVersion from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class MockView: diff --git a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py index 8a64a025..d563e81d 100644 --- a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py +++ b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -15,6 +16,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from itam.models.device import Device, DeviceSoftware from itam.models.software import Software +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/inventory/test_inventory_viewset.py b/app/itam/tests/functional/inventory/test_inventory_viewset.py index f5089a5b..a904f99c 100644 --- a/app/itam/tests/functional/inventory/test_inventory_viewset.py +++ b/app/itam/tests/functional/inventory/test_inventory_viewset.py @@ -1,9 +1,10 @@ import copy +import django import pytest from unittest.mock import patch -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -24,6 +25,8 @@ from itam.tasks.inventory import process_inventory from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/operating_system/test_operating_system_viewset.py b/app/itam/tests/functional/operating_system/test_operating_system_viewset.py index a4bf6e59..e0f638ae 100644 --- a/app/itam/tests/functional/operating_system/test_operating_system_viewset.py +++ b/app/itam/tests/functional/operating_system/test_operating_system_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,7 @@ from itam.models.operating_system import OperatingSystem from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() diff --git a/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py b/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py index 5e10ca6c..683d6034 100644 --- a/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py +++ b/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,7 @@ from itam.models.operating_system import OperatingSystem, OperatingSystemVersion from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() diff --git a/app/itam/tests/functional/software/test_software_viewset.py b/app/itam/tests/functional/software/test_software_viewset.py index eef75e58..a8c2aa26 100644 --- a/app/itam/tests/functional/software/test_software_viewset.py +++ b/app/itam/tests/functional/software/test_software_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,8 @@ from itam.models.software import Software from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/software_category/test_software_category_viewset.py b/app/itam/tests/functional/software_category/test_software_category_viewset.py index 5f09528b..67284e8a 100644 --- a/app/itam/tests/functional/software_category/test_software_category_viewset.py +++ b/app/itam/tests/functional/software_category/test_software_category_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,8 @@ from itam.models.software import SoftwareCategory from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/functional/software_version/test_software_version_viewset.py b/app/itam/tests/functional/software_version/test_software_version_viewset.py index 131e5c67..94361700 100644 --- a/app/itam/tests/functional/software_version/test_software_version_viewset.py +++ b/app/itam/tests/functional/software_version/test_software_version_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,8 @@ from itam.models.software import Software, SoftwareVersion from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/itam/tests/unit/device/test_device_api.py b/app/itam/tests/unit/device/test_device_api.py index 12fd4bf6..eae9868a 100644 --- a/app/itam/tests/unit/device/test_device_api.py +++ b/app/itam/tests/unit/device/test_device_api.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,9 @@ from config_management.models.groups import ConfigGroups, ConfigGroupHosts from itam.models.device import Device +User = django.contrib.auth.get_user_model() + + class DeviceAPI(TestCase): diff --git a/app/itam/tests/unit/device/test_device_api_v2.py b/app/itam/tests/unit/device/test_device_api_v2.py index fc89aa27..a933a232 100644 --- a/app/itam/tests/unit/device/test_device_api_v2.py +++ b/app/itam/tests/unit/device/test_device_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,8 @@ from core.models.manufacturer import Manufacturer from itam.models.device import Device, DeviceModel, DeviceType +User = django.contrib.auth.get_user_model() + class DeviceAPI( diff --git a/app/itam/tests/unit/device/test_device_permission.py b/app/itam/tests/unit/device/test_device_permission.py index 3361d305..c2596ac8 100644 --- a/app/itam/tests/unit/device/test_device_permission.py +++ b/app/itam/tests/unit/device/test_device_permission.py @@ -1,6 +1,6 @@ -# from django.conf import settings -# from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +import django + +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase @@ -17,6 +17,8 @@ from app.tests.abstract.model_permissions import ModelPermissions from itam.models.device import Device +User = django.contrib.auth.get_user_model() + class DevicePermissions(TestCase, ModelPermissions): diff --git a/app/itam/tests/unit/device/test_device_permission_api.py b/app/itam/tests/unit/device/test_device_permission_api.py index 1816987f..7ec91578 100644 --- a/app/itam/tests/unit/device/test_device_permission_api.py +++ b/app/itam/tests/unit/device/test_device_permission_api.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -13,6 +14,9 @@ from api.tests.abstract.api_permissions import APIPermissions from itam.models.device import Device +User = django.contrib.auth.get_user_model() + + class DevicePermissionsAPI(TestCase, APIPermissions): diff --git a/app/itam/tests/unit/device_model/test_device_model_api_v2.py b/app/itam/tests/unit/device_model/test_device_model_api_v2.py index c31f478e..ee6ba9ac 100644 --- a/app/itam/tests/unit/device_model/test_device_model_api_v2.py +++ b/app/itam/tests/unit/device_model/test_device_model_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,8 @@ from core.models.manufacturer import Manufacturer from itam.models.device import DeviceModel +User = django.contrib.auth.get_user_model() + class DeviceModelAPI( diff --git a/app/itam/tests/unit/device_model/test_device_model_permission.py b/app/itam/tests/unit/device_model/test_device_model_permission.py index be7e337d..e41a63e5 100644 --- a/app/itam/tests/unit/device_model/test_device_model_permission.py +++ b/app/itam/tests/unit/device_model/test_device_model_permission.py @@ -1,6 +1,6 @@ -# from django.conf import settings -# from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +import django + +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +17,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from itam.models.device import DeviceModel +User = django.contrib.auth.get_user_model() + + class DeviceModelPermissions(TestCase, ModelPermissions): diff --git a/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py b/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py index 1cd47c37..1a2775f6 100644 --- a/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py +++ b/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -19,6 +20,8 @@ from core.models.manufacturer import Manufacturer from itam.serializers.device_operating_system import Device, DeviceOperatingSystem, DeviceOperatingSystemModelSerializer from itam.models.operating_system import OperatingSystem, OperatingSystemVersion +User = django.contrib.auth.get_user_model() + class DeviceOperatingSystemAPI( diff --git a/app/itam/tests/unit/device_software/test_device_software_api_v2.py b/app/itam/tests/unit/device_software/test_device_software_api_v2.py index 960eed50..0583a5aa 100644 --- a/app/itam/tests/unit/device_software/test_device_software_api_v2.py +++ b/app/itam/tests/unit/device_software/test_device_software_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -19,6 +20,8 @@ from core.models.manufacturer import Manufacturer from itam.models.device import Device, DeviceSoftware from itam.models.software import Software, SoftwareCategory, SoftwareVersion +User = django.contrib.auth.get_user_model() + class DeviceSoftwareAPI( diff --git a/app/itam/tests/unit/device_type/test_device_type_api_v2.py b/app/itam/tests/unit/device_type/test_device_type_api_v2.py index ce0b24ed..d42a99c1 100644 --- a/app/itam/tests/unit/device_type/test_device_type_api_v2.py +++ b/app/itam/tests/unit/device_type/test_device_type_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_fields import APITenancyObject from itam.models.device import DeviceType +User = django.contrib.auth.get_user_model() + class DeviceTypeAPI( diff --git a/app/itam/tests/unit/device_type/test_device_type_permission.py b/app/itam/tests/unit/device_type/test_device_type_permission.py index f6df9af4..896bfc59 100644 --- a/app/itam/tests/unit/device_type/test_device_type_permission.py +++ b/app/itam/tests/unit/device_type/test_device_type_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from itam.models.device import DeviceType +User = django.contrib.auth.get_user_model() + + class DeviceTypePermissions(TestCase, ModelPermissions): diff --git a/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py b/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py index 1d77cf8a..70e8a764 100644 --- a/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py +++ b/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -19,6 +20,8 @@ from core.models.manufacturer import Manufacturer from itam.serializers.device_operating_system import Device, DeviceOperatingSystem, DeviceOperatingSystemModelSerializer from itam.models.operating_system import OperatingSystem, OperatingSystemVersion +User = django.contrib.auth.get_user_model() + class OperatingSystemInstallsAPI( diff --git a/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py b/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py index 1979a66c..7830f9ce 100644 --- a/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py +++ b/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -19,6 +20,8 @@ from core.models.manufacturer import Manufacturer from itam.models.device import Device, DeviceSoftware from itam.models.software import Software, SoftwareCategory, SoftwareVersion +User = django.contrib.auth.get_user_model() + class SoftwareInstallsAPI( diff --git a/app/itam/tests/unit/inventory/test_api_inventory.py b/app/itam/tests/unit/inventory/test_api_inventory.py index b8fe4454..3b254d22 100644 --- a/app/itam/tests/unit/inventory/test_api_inventory.py +++ b/app/itam/tests/unit/inventory/test_api_inventory.py @@ -1,9 +1,10 @@ import datetime +import django import json import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -25,6 +26,8 @@ from itam.tasks.inventory import process_inventory from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class InventoryAPI(TestCase): diff --git a/app/itam/tests/unit/inventory/test_inventory_permission_api.py b/app/itam/tests/unit/inventory/test_inventory_permission_api.py index 5c7d1a06..ca826245 100644 --- a/app/itam/tests/unit/inventory/test_inventory_permission_api.py +++ b/app/itam/tests/unit/inventory/test_inventory_permission_api.py @@ -1,10 +1,11 @@ import celery +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -20,6 +21,10 @@ from itam.models.device import Device from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + + + class InventoryPermissionsAPI(TestCase): model = Device diff --git a/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py b/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py index b35d1811..cdccaa0c 100644 --- a/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py +++ b/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,8 @@ from core.models.manufacturer import Manufacturer from itam.models.operating_system import OperatingSystem +User = django.contrib.auth.get_user_model() + class OperatingSystemAPI( diff --git a/app/itam/tests/unit/operating_system/test_operating_system_permission.py b/app/itam/tests/unit/operating_system/test_operating_system_permission.py index a559371a..37fd8b93 100644 --- a/app/itam/tests/unit/operating_system/test_operating_system_permission.py +++ b/app/itam/tests/unit/operating_system/test_operating_system_permission.py @@ -1,6 +1,6 @@ -# from django.conf import settings +import django from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +17,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from itam.models.operating_system import OperatingSystem +User = django.contrib.auth.get_user_model() + + class OperatingSystemPermissions(TestCase, ModelPermissions): diff --git a/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py b/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py index a5753284..e1575e76 100644 --- a/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py +++ b/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +17,8 @@ from api.tests.abstract.api_fields import APITenancyObject from itam.models.operating_system import OperatingSystem, OperatingSystemVersion +User = django.contrib.auth.get_user_model() + class OperatingSystemVersionAPI( diff --git a/app/itam/tests/unit/software/test_software_api.py b/app/itam/tests/unit/software/test_software_api.py index 8f842b2f..e40214d8 100644 --- a/app/itam/tests/unit/software/test_software_api.py +++ b/app/itam/tests/unit/software/test_software_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from core.models.manufacturer import Manufacturer from itam.models.software import Software, SoftwareCategory +User = django.contrib.auth.get_user_model() + class SoftwareAPI(TestCase): diff --git a/app/itam/tests/unit/software/test_software_api_v2.py b/app/itam/tests/unit/software/test_software_api_v2.py index 01d9d43b..091d3eee 100644 --- a/app/itam/tests/unit/software/test_software_api_v2.py +++ b/app/itam/tests/unit/software/test_software_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,8 @@ from core.models.manufacturer import Manufacturer from itam.models.software import Software, SoftwareCategory +User = django.contrib.auth.get_user_model() + class SoftwareAPI( diff --git a/app/itam/tests/unit/software/test_software_permission.py b/app/itam/tests/unit/software/test_software_permission.py index b8795ce1..337d86bb 100644 --- a/app/itam/tests/unit/software/test_software_permission.py +++ b/app/itam/tests/unit/software/test_software_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from itam.models.software import Software +User = django.contrib.auth.get_user_model() + + class SoftwarePermissions(TestCase, ModelPermissions): diff --git a/app/itam/tests/unit/software/test_software_permission_api.py b/app/itam/tests/unit/software/test_software_permission_api.py index 1aff0815..878b69f5 100644 --- a/app/itam/tests/unit/software/test_software_permission_api.py +++ b/app/itam/tests/unit/software/test_software_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,9 @@ from api.tests.abstract.api_permissions import APIPermissions from itam.models.software import Software +User = django.contrib.auth.get_user_model() + + class SoftwarePermissionsAPI(TestCase, APIPermissions): diff --git a/app/itam/tests/unit/software_category/test_software_category_api_v2.py b/app/itam/tests/unit/software_category/test_software_category_api_v2.py index 6a2f0235..3314f86c 100644 --- a/app/itam/tests/unit/software_category/test_software_category_api_v2.py +++ b/app/itam/tests/unit/software_category/test_software_category_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,8 @@ from core.models.manufacturer import Manufacturer from itam.models.software import Software, SoftwareCategory +User = django.contrib.auth.get_user_model() + class SoftwareCategoryAPI( diff --git a/app/itam/tests/unit/software_category/test_software_category_permission.py b/app/itam/tests/unit/software_category/test_software_category_permission.py index 02eadab1..738fe1be 100644 --- a/app/itam/tests/unit/software_category/test_software_category_permission.py +++ b/app/itam/tests/unit/software_category/test_software_category_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from itam.models.software import SoftwareCategory +User = django.contrib.auth.get_user_model() + + class SoftwareCategoryPermissions(TestCase, ModelPermissions): diff --git a/app/itam/tests/unit/software_version/test_software_version_api_v2.py b/app/itam/tests/unit/software_version/test_software_version_api_v2.py index 1ff41c14..aea0a9d9 100644 --- a/app/itam/tests/unit/software_version/test_software_version_api_v2.py +++ b/app/itam/tests/unit/software_version/test_software_version_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +17,8 @@ from api.tests.abstract.api_fields import APITenancyObject from itam.models.software import Software, SoftwareVersion +User = django.contrib.auth.get_user_model() + class SoftwareVersionCategoryAPI( From a4772e3c25cd87f9a2f5f5f59868a255b37e8ac8 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 19:40:54 +0930 Subject: [PATCH 127/175] refactor(project_management): Update all references to `User` to use `get_user_model()` ref: #756 #755 --- app/project_management/models/project_milestone.py | 1 - app/project_management/models/projects.py | 6 +++--- .../tests/functional/project/test_project_viewset.py | 5 ++++- .../project_milestone/test_project_milestone_viewset.py | 5 ++++- .../project_state/test_project_state_viewset.py | 5 ++++- .../functional/project_type/test_project_type_viewset.py | 5 ++++- .../tests/unit/project/test_project_api_v2.py | 5 ++++- .../tests/unit/project/test_project_permission.py | 7 +++++-- .../tests/unit/project/test_project_permission_api.py | 6 +++++- .../project_milestone/test_project_milestone_api_v2.py | 5 ++++- .../test_project_milestone_permission.py | 8 ++++++-- .../test_project_milestone_permission_api.py | 6 +++++- .../tests/unit/project_state/test_project_state_api_v2.py | 5 ++++- .../unit/project_state/test_project_state_permission.py | 7 +++++-- .../project_state/test_project_state_permission_api.py | 5 ++++- .../tests/unit/project_type/test_project_type_api_v2.py | 5 ++++- .../unit/project_type/test_project_type_permission.py | 8 ++++++-- .../unit/project_type/test_project_type_permission_api.py | 5 ++++- 18 files changed, 75 insertions(+), 24 deletions(-) diff --git a/app/project_management/models/project_milestone.py b/app/project_management/models/project_milestone.py index 86ad1966..c804ef80 100644 --- a/app/project_management/models/project_milestone.py +++ b/app/project_management/models/project_milestone.py @@ -1,4 +1,3 @@ -from django.contrib.auth.models import User from django.db import models from access.fields import AutoCreatedField diff --git a/app/project_management/models/projects.py b/app/project_management/models/projects.py index 436e12fa..29ac27c2 100644 --- a/app/project_management/models/projects.py +++ b/app/project_management/models/projects.py @@ -1,4 +1,4 @@ -from django.contrib.auth.models import User +from django.conf import settings from django.db import models from access.models.team import Team @@ -152,7 +152,7 @@ class Project(ProjectCommonFieldsName): ) manager_user = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= True, help_text = 'User who is the Project Manager', on_delete=models.SET_NULL, @@ -173,7 +173,7 @@ class Project(ProjectCommonFieldsName): model_notes = None team_members = models.ManyToManyField( - to = User, + to = settings.AUTH_USER_MODEL, blank = True, ) diff --git a/app/project_management/tests/functional/project/test_project_viewset.py b/app/project_management/tests/functional/project/test_project_viewset.py index 4dcf23d9..852b0538 100644 --- a/app/project_management/tests/functional/project/test_project_viewset.py +++ b/app/project_management/tests/functional/project/test_project_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from project_management.models.projects import Project from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py b/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py index f648eec9..29bf50d2 100644 --- a/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py +++ b/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from project_management.models.project_milestone import Project, ProjectMilestone +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/project_management/tests/functional/project_state/test_project_state_viewset.py b/app/project_management/tests/functional/project_state/test_project_state_viewset.py index 4c6e22d3..c63350b2 100644 --- a/app/project_management/tests/functional/project_state/test_project_state_viewset.py +++ b/app/project_management/tests/functional/project_state/test_project_state_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,8 @@ from project_management.models.project_states import ProjectState from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/project_management/tests/functional/project_type/test_project_type_viewset.py b/app/project_management/tests/functional/project_type/test_project_type_viewset.py index 2d2183a3..431734ab 100644 --- a/app/project_management/tests/functional/project_type/test_project_type_viewset.py +++ b/app/project_management/tests/functional/project_type/test_project_type_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -16,6 +17,8 @@ from project_management.models.project_types import ProjectType from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/project_management/tests/unit/project/test_project_api_v2.py b/app/project_management/tests/unit/project/test_project_api_v2.py index d43837e2..9543410f 100644 --- a/app/project_management/tests/unit/project/test_project_api_v2.py +++ b/app/project_management/tests/unit/project/test_project_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -22,6 +23,8 @@ from project_management.models.projects import Project, ProjectState, ProjectTyp from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ProjectAPI( diff --git a/app/project_management/tests/unit/project/test_project_permission.py b/app/project_management/tests/unit/project/test_project_permission.py index 278e77a2..2706c59d 100644 --- a/app/project_management/tests/unit/project/test_project_permission.py +++ b/app/project_management/tests/unit/project/test_project_permission.py @@ -1,6 +1,6 @@ -# from django.conf import settings +import django from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +17,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from project_management.models.projects import Project +User = django.contrib.auth.get_user_model() + + class ProjectPermissions(TestCase, ModelPermissions): diff --git a/app/project_management/tests/unit/project/test_project_permission_api.py b/app/project_management/tests/unit/project/test_project_permission_api.py index d8f09306..4093e9ce 100644 --- a/app/project_management/tests/unit/project/test_project_permission_api.py +++ b/app/project_management/tests/unit/project/test_project_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,9 @@ from api.tests.abstract.api_permissions import APIPermissions from core.models.ticket.ticket_comment_category import TicketCommentCategory +User = django.contrib.auth.get_user_model() + + class TicketCommentCategoryPermissionsAPI(TestCase, APIPermissions): diff --git a/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py b/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py index 2939ada1..4da82091 100644 --- a/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py +++ b/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -18,6 +19,8 @@ from project_management.models.project_milestone import Project, ProjectMileston from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ProjectMilestoneAPI( diff --git a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py index 0725ef67..58f155a4 100644 --- a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py +++ b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from project_management.models.project_milestone import Project, ProjectMilestone +User = django.contrib.auth.get_user_model() + + class ProjectMilestonePermissions(TestCase, ModelPermissions): diff --git a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py index 47a3c37c..8170cff0 100644 --- a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py +++ b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -16,6 +17,9 @@ from api.tests.abstract.api_permissions import APIPermissions from project_management.models.projects import Project from project_management.models.project_milestone import Project, ProjectMilestone +User = django.contrib.auth.get_user_model() + + class ProjectMilestonePermissionsAPI(TestCase, APIPermissions): diff --git a/app/project_management/tests/unit/project_state/test_project_state_api_v2.py b/app/project_management/tests/unit/project_state/test_project_state_api_v2.py index a39e4c9e..25845206 100644 --- a/app/project_management/tests/unit/project_state/test_project_state_api_v2.py +++ b/app/project_management/tests/unit/project_state/test_project_state_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -20,6 +21,8 @@ from project_management.models.projects import ProjectState from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ProjectStateAPI( diff --git a/app/project_management/tests/unit/project_state/test_project_state_permission.py b/app/project_management/tests/unit/project_state/test_project_state_permission.py index 0df46b85..9aa2145e 100644 --- a/app/project_management/tests/unit/project_state/test_project_state_permission.py +++ b/app/project_management/tests/unit/project_state/test_project_state_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,8 @@ from app.tests.abstract.model_permissions import ModelPermissions from project_management.models.project_states import ProjectState +User = django.contrib.auth.get_user_model() + class ProjectStatePermissions(TestCase, ModelPermissions): diff --git a/app/project_management/tests/unit/project_state/test_project_state_permission_api.py b/app/project_management/tests/unit/project_state/test_project_state_permission_api.py index 2186edec..dd1716b4 100644 --- a/app/project_management/tests/unit/project_state/test_project_state_permission_api.py +++ b/app/project_management/tests/unit/project_state/test_project_state_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_permissions import APIPermissions from project_management.models.project_states import ProjectState +User = django.contrib.auth.get_user_model() + class ProjectStatePermissionsAPI(TestCase, APIPermissions): diff --git a/app/project_management/tests/unit/project_type/test_project_type_api_v2.py b/app/project_management/tests/unit/project_type/test_project_type_api_v2.py index f7ae406f..9f4617ef 100644 --- a/app/project_management/tests/unit/project_type/test_project_type_api_v2.py +++ b/app/project_management/tests/unit/project_type/test_project_type_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -20,6 +21,8 @@ from project_management.models.project_types import ProjectType from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ProjectTypeAPI( diff --git a/app/project_management/tests/unit/project_type/test_project_type_permission.py b/app/project_management/tests/unit/project_type/test_project_type_permission.py index f02737f4..75ec40de 100644 --- a/app/project_management/tests/unit/project_type/test_project_type_permission.py +++ b/app/project_management/tests/unit/project_type/test_project_type_permission.py @@ -1,6 +1,7 @@ -# from django.conf import settings +import django + from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +18,9 @@ from app.tests.abstract.model_permissions import ModelPermissions from project_management.models.project_types import ProjectType +User = django.contrib.auth.get_user_model() + + class ProjectTypePermissions(TestCase, ModelPermissions): diff --git a/app/project_management/tests/unit/project_type/test_project_type_permission_api.py b/app/project_management/tests/unit/project_type/test_project_type_permission_api.py index 50e2af2e..f689bffe 100644 --- a/app/project_management/tests/unit/project_type/test_project_type_permission_api.py +++ b/app/project_management/tests/unit/project_type/test_project_type_permission_api.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -14,6 +15,8 @@ from api.tests.abstract.api_permissions import APIPermissions from project_management.models.project_types import ProjectType +User = django.contrib.auth.get_user_model() + class ProjectTypePermissionsAPI(TestCase, APIPermissions): From 2e49de85734e06e80dd780b2957e78b35a827c3e Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 19:47:50 +0930 Subject: [PATCH 128/175] refactor(settings): Update all references to `User` to use `get_user_model()` ref: #756 closes #755 --- app/settings/forms/admin_settings_global.py | 2 -- app/settings/forms/app_settings.py | 2 -- app/settings/forms/external_links.py | 1 - app/settings/forms/user_settings.py | 2 -- app/settings/models/user_settings.py | 6 ++++-- .../app_settings/test_app_settings_viewset.py | 5 ++++- .../test_external_link_viewset.py | 5 ++++- .../test_user_settings_viewset.py | 5 ++++- .../app_settings/test_app_settings_api_v2.py | 5 ++++- .../test_app_settings_permission.py | 18 ++++++++++-------- .../test_external_links_api_v2.py | 5 ++++- .../test_external_links_permission.py | 5 ++++- .../test_unit_user_settings_model.py | 8 ++++++-- .../user_settings/test_user_settings_api_v2.py | 5 ++++- .../test_user_settings_permissions.py | 8 ++++++-- 15 files changed, 54 insertions(+), 28 deletions(-) diff --git a/app/settings/forms/admin_settings_global.py b/app/settings/forms/admin_settings_global.py index fc4e6a7e..c5bd00e0 100644 --- a/app/settings/forms/admin_settings_global.py +++ b/app/settings/forms/admin_settings_global.py @@ -1,8 +1,6 @@ from django import forms from django.db.models import Q -from django.contrib.auth.models import User - from access.models.organization import Organization from access.models.team_user import TeamUsers diff --git a/app/settings/forms/app_settings.py b/app/settings/forms/app_settings.py index 303c3399..83f19782 100644 --- a/app/settings/forms/app_settings.py +++ b/app/settings/forms/app_settings.py @@ -1,8 +1,6 @@ from django import forms from django.db.models import Q -from django.contrib.auth.models import User - from access.models.organization import Organization from access.models.team_user import TeamUsers diff --git a/app/settings/forms/external_links.py b/app/settings/forms/external_links.py index ce56f13e..648dda8c 100644 --- a/app/settings/forms/external_links.py +++ b/app/settings/forms/external_links.py @@ -1,6 +1,5 @@ from django import forms -# from django.contrib.auth.models import User from django.urls import reverse from access.models.organization import Organization diff --git a/app/settings/forms/user_settings.py b/app/settings/forms/user_settings.py index 8daa7ee1..fca139ac 100644 --- a/app/settings/forms/user_settings.py +++ b/app/settings/forms/user_settings.py @@ -1,8 +1,6 @@ from django import forms from django.db.models import Q -from django.contrib.auth.models import User - from access.models.organization import Organization from access.models.team_user import TeamUsers diff --git a/app/settings/models/user_settings.py b/app/settings/models/user_settings.py index 9e62e52d..87d59fd2 100644 --- a/app/settings/models/user_settings.py +++ b/app/settings/models/user_settings.py @@ -1,8 +1,8 @@ +import django import zoneinfo from rest_framework.reverse import reverse -from django.contrib.auth.models import User from django.conf import settings from django.db import models from django.db.models.signals import post_save @@ -20,6 +20,8 @@ TIMEZONES = tuple(zip( sorted_timezones )) +User = django.contrib.auth.get_user_model() + class UserSettingsCommonFields(models.Model): @@ -64,7 +66,7 @@ class UserSettings(UserSettingsCommonFields): user = models.ForeignKey( - User, + settings.AUTH_USER_MODEL, blank= False, help_text = 'User this Setting belongs to', on_delete=models.CASCADE, diff --git a/app/settings/tests/functional/app_settings/test_app_settings_viewset.py b/app/settings/tests/functional/app_settings/test_app_settings_viewset.py index 51bbc0b1..e1c93975 100644 --- a/app/settings/tests/functional/app_settings/test_app_settings_viewset.py +++ b/app/settings/tests/functional/app_settings/test_app_settings_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -25,6 +26,8 @@ from api.tests.abstract.test_metadata_functional import ( from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/settings/tests/functional/external_links/test_external_link_viewset.py b/app/settings/tests/functional/external_links/test_external_link_viewset.py index 3543c177..dcc392c8 100644 --- a/app/settings/tests/functional/external_links/test_external_link_viewset.py +++ b/app/settings/tests/functional/external_links/test_external_link_viewset.py @@ -1,10 +1,11 @@ +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -18,6 +19,8 @@ from api.tests.abstract.test_metadata_functional import MetadataAttributesFuncti from settings.models.external_link import ExternalLink +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/settings/tests/functional/user_settings/test_user_settings_viewset.py b/app/settings/tests/functional/user_settings/test_user_settings_viewset.py index 29217a88..6125b54a 100644 --- a/app/settings/tests/functional/user_settings/test_user_settings_viewset.py +++ b/app/settings/tests/functional/user_settings/test_user_settings_viewset.py @@ -1,6 +1,7 @@ +import django import pytest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -26,6 +27,8 @@ from api.tests.abstract.test_metadata_functional import ( from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ViewSetBase: diff --git a/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py b/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py index 0750fdd0..117a6e5e 100644 --- a/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py +++ b/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from api.tests.abstract.api_fields import APICommonFields from settings.models.app_settings import AppSettings from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class AppSettingsAPI( diff --git a/app/settings/tests/unit/app_settings/test_app_settings_permission.py b/app/settings/tests/unit/app_settings/test_app_settings_permission.py index d045bffc..57ff676a 100644 --- a/app/settings/tests/unit/app_settings/test_app_settings_permission.py +++ b/app/settings/tests/unit/app_settings/test_app_settings_permission.py @@ -1,23 +1,25 @@ -# from django.conf import settings -from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User -from django.contrib.contenttypes.models import ContentType -from django.shortcuts import reverse -from django.test import TestCase, Client - +import django import pytest import unittest import requests +from django.contrib.auth import get_user_model +from django.contrib.auth.models import AnonymousUser, Permission +from django.contrib.contenttypes.models import ContentType +from django.shortcuts import reverse +from django.test import TestCase, Client + from access.models.organization import Organization from access.models.team import Team from access.models.team_user import TeamUsers from app.tests.abstract.model_permissions import ModelPermissionsView - from settings.models.app_settings import AppSettings +User = django.contrib.auth.get_user_model() + + class AppSettingsPermissions(TestCase, ModelPermissionsView): diff --git a/app/settings/tests/unit/external_links/test_external_links_api_v2.py b/app/settings/tests/unit/external_links/test_external_links_api_v2.py index 21cfcdb5..68465e88 100644 --- a/app/settings/tests/unit/external_links/test_external_links_api_v2.py +++ b/app/settings/tests/unit/external_links/test_external_links_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from api.tests.abstract.api_fields import APITenancyObject from settings.models.external_link import ExternalLink from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class ExternalLinkAPI( diff --git a/app/settings/tests/unit/external_links/test_external_links_permission.py b/app/settings/tests/unit/external_links/test_external_links_permission.py index d5117862..f001c7ae 100644 --- a/app/settings/tests/unit/external_links/test_external_links_permission.py +++ b/app/settings/tests/unit/external_links/test_external_links_permission.py @@ -1,8 +1,9 @@ +import django import pytest import unittest import requests -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase @@ -15,6 +16,8 @@ from app.tests.abstract.model_permissions import ModelPermissions from settings.models.external_link import ExternalLink +User = django.contrib.auth.get_user_model() + class ExternalLinkPermissions(TestCase, ModelPermissions): diff --git a/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py b/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py index 72a12ec3..cc446a54 100644 --- a/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py +++ b/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py @@ -1,10 +1,10 @@ - +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -15,6 +15,10 @@ from access.models.team_user import TeamUsers from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + + + class UserSettings(TestCase): diff --git a/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py b/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py index 59cc496c..55f13567 100644 --- a/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py +++ b/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py @@ -1,7 +1,8 @@ +import django import pytest import unittest -from django.contrib.auth.models import Permission, User +from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase @@ -17,6 +18,8 @@ from api.tests.abstract.api_fields import APICommonFields from settings.models.app_settings import AppSettings from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + class UserSettingsAPI( diff --git a/app/settings/tests/unit/user_settings/test_user_settings_permissions.py b/app/settings/tests/unit/user_settings/test_user_settings_permissions.py index 0efaf96c..3413d5cc 100644 --- a/app/settings/tests/unit/user_settings/test_user_settings_permissions.py +++ b/app/settings/tests/unit/user_settings/test_user_settings_permissions.py @@ -1,10 +1,10 @@ - +import django import pytest import unittest import requests from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser, Permission, User +from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client @@ -17,6 +17,10 @@ from app.tests.abstract.model_permissions import ModelPermissionsView from settings.models.user_settings import UserSettings +User = django.contrib.auth.get_user_model() + + + class UserSettingsPermissions(TestCase, ModelPermissionsView): From 2d7335ff8536f0c74abb169d665423d880de7b48 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 14 May 2025 23:22:48 +0930 Subject: [PATCH 129/175] refactor(access): Rename model Organization -> Tenant ref: #756 #505 --- .gitignore | 2 +- Release-Notes.md | 4 + app/access/admin.py | 2 +- app/access/forms/organization.py | 2 +- app/access/middleware/request.py | 2 +- .../0007_rename_organization_tenant.py | 26 +++ ...ions_alter_entity_organization_and_more.py | 47 +++++ app/access/mixin.py | 6 +- app/access/mixins/organization.py | 4 +- app/access/mixins/permissions.py | 8 +- app/access/models/organization.py | 160 +---------------- app/access/models/organization_history.py | 8 +- app/access/models/organization_notes.py | 4 +- app/access/models/team.py | 8 +- app/access/models/team_user.py | 4 +- app/access/models/tenancy.py | 12 +- app/access/models/tenant.py | 161 ++++++++++++++++++ app/access/serializers/entity.py | 4 +- app/access/serializers/entity_contact.py | 4 +- app/access/serializers/entity_person.py | 4 +- app/access/serializers/organization.py | 15 +- app/access/serializers/role.py | 4 +- app/access/serializers/teams.py | 4 +- .../test_functional_entity_serializer.py | 2 +- .../entity/test_functional_entity_viewset.py | 2 +- .../organization/test_organization_history.py | 2 +- .../test_organization_serializer.py | 12 +- .../organization/test_organization_viewset.py | 4 +- .../test_organization_notes_api_v2.py | 2 +- .../role/test_functional_role_serializer.py | 2 +- .../role/test_functional_role_viewset.py | 2 +- .../team/test_team_permission_viewset.py | 2 +- .../functional/team/test_team_serializer.py | 2 +- .../test_team_user_permission_viewset.py | 2 +- .../team_user/test_team_user_serializer.py | 2 +- .../unit/entity/test_unit_entity_api_v2.py | 2 +- .../tests/unit/mixin/test_permission.py | 2 +- .../organization/test_organizaiton_api.py | 2 +- .../organization/test_organizaiton_api_v2.py | 2 +- .../test_organizaiton_permission.py | 10 +- .../test_organizaiton_permission_api.py | 2 +- .../unit/organization/test_organization.py | 2 +- .../test_unit_organization_history_api_v2.py | 2 +- .../tests/unit/role/test_unit_role_api_v2.py | 2 +- app/access/tests/unit/team/test_team_api.py | 2 +- .../tests/unit/team/test_team_api_v2.py | 2 +- .../tests/unit/team/test_team_permission.py | 2 +- .../unit/team/test_team_permission_api.py | 2 +- .../unit/team_user/test_team_user_api_v2.py | 2 +- .../team_user/test_team_user_functions.py | 2 +- .../team_user/test_team_user_permission.py | 2 +- .../team_user/test_unit_team_user_model.py | 2 +- .../test_unit_tenancy_object_model.py | 2 +- app/access/views/organization.py | 8 +- app/access/views/team.py | 2 +- app/access/viewsets/organization.py | 18 +- app/access/viewsets/team.py | 2 +- .../0004_alter_assetbase_organization.py | 21 +++ app/accounting/serializers/asset.py | 4 +- .../test_functional_asset_base_metadata.py | 2 +- .../test_functional_asset_base_viewset.py | 2 +- app/api/react_ui_metadata.py | 11 +- app/api/serializers/access.py | 2 +- app/api/serializers/auth_token.py | 4 +- app/api/serializers/common.py | 4 +- .../test_functional_auth_token_serializer.py | 2 +- .../test_functional_auth_token_viewset.py | 2 +- app/api/tests/unit/test_index_viewset.py | 2 +- app/api/tests/unit/test_navigation_menu.py | 16 +- .../tests/unit/test_unit_common_viewset.py | 2 +- app/api/tests/unit/token/test_token.py | 2 +- .../unit/token/test_unit_auth_token_api_v2.py | 2 +- app/api/views/access.py | 2 +- app/api/views/mixin.py | 2 +- app/app/context_processors/base.py | 2 +- app/app/tests/abstract/mock_view.py | 2 +- app/app/tests/unit/test_unit_models.py | 2 +- ...ter_knowledgebase_organization_and_more.py | 31 ++++ app/assistance/serializers/knowledge_base.py | 4 +- .../serializers/knowledge_base_category.py | 4 +- .../model_knowledge_base_article.py | 4 +- .../test_knowledge_base_serializer.py | 2 +- .../test_knowledge_base_viewset.py | 2 +- ...test_knowledge_base_category_serializer.py | 2 +- .../test_knowledge_base_category_viewset.py | 2 +- ...model_knowledge_base_article_serializer.py | 2 +- ...st_model_knowledge_base_article_viewset.py | 2 +- .../test_knowledge_base_api_v2.py | 2 +- .../test_knowledge_base_permission.py | 2 +- .../test_knowledge_base_category_api_v2.py | 2 +- ...test_knowledge_base_category_permission.py | 2 +- ...est_model_knowledge_base_article_api_v2.py | 2 +- ...unit_model_knowledge_base_article_model.py | 2 +- ..._configgrouphosts_organization_and_more.py | 31 ++++ .../serializers/config_group.py | 4 +- .../serializers/config_group_software.py | 4 +- .../test_config_groups_serializer.py | 2 +- .../test_config_groups_viewset.py | 2 +- .../test_config_groups_software_serializer.py | 2 +- .../test_config_groups_software_viewset.py | 2 +- .../config_groups/test_config_groups_api.py | 2 +- .../test_config_groups_api_v2.py | 2 +- .../test_config_groups_permission.py | 2 +- .../test_unit_config_groups_model.py | 2 +- .../test_config_groups_software_api_v2.py | 2 +- .../test_config_groups_software_permission.py | 2 +- .../test_unit_config_groups_software_model.py | 2 +- app/conftest.py | 6 +- app/core/forms/common.py | 2 +- app/core/lib/slash_commands/linked_model.py | 6 +- .../0016_data_move_history_to_new_table.py | 4 +- ...lter_manufacturer_organization_and_more.py | 76 +++++++++ app/core/mixin/history_save.py | 2 +- app/core/models/model_history.py | 8 +- app/core/models/ticket/ticket_linked_items.py | 6 +- app/core/serializers/celery_log.py | 2 +- app/core/serializers/history.py | 4 +- app/core/serializers/manufacturer.py | 4 +- app/core/serializers/model_notes.py | 6 +- app/core/serializers/ticket.py | 4 +- app/core/serializers/ticket_category.py | 4 +- app/core/serializers/ticket_comment.py | 4 +- .../serializers/ticket_comment_category.py | 4 +- .../serializers/ticket_comment_depreciated.py | 4 +- app/core/serializers/ticket_depreciated.py | 4 +- app/core/serializers/ticket_linked_item.py | 10 +- app/core/serializers/ticket_related.py | 4 +- .../tests/abstract/model_notes_api_fields.py | 2 +- .../tests/abstract/test_functional_history.py | 2 +- .../abstract/test_functional_notes_viewset.py | 2 +- .../tests/abstract/test_item_ticket_api_v2.py | 2 +- app/core/tests/abstract/test_ticket_api_v2.py | 2 +- .../tests/abstract/test_ticket_serializer.py | 2 +- .../tests/abstract/test_ticket_viewset.py | 2 +- .../test_unit_model_history_api_v2.py | 2 +- .../test_unit_model_notes_serializer.py | 2 +- .../test_manufacturer_serializer.py | 2 +- .../manufacturer/test_manufacturer_viewset.py | 2 +- .../test_related_ticket_serializer.py | 2 +- .../test_related_ticket_viewset.py | 2 +- .../test_slash_command_related.py | 2 +- .../test_functional_history_viewset.py | 2 +- .../test_task_result_viewset.py | 2 +- .../test_functional_ticket_base_metadata.py | 2 +- .../test_functional_ticket_base_viewset.py | 2 +- .../test_ticket_category_serializer.py | 2 +- .../test_ticket_category_viewset.py | 2 +- .../test_ticket_comment_serializer.py | 2 +- .../test_ticket_comment_viewset.py | 2 +- ...test_ticket_comment_category_serializer.py | 2 +- .../test_ticket_comment_category_viewset.py | 2 +- .../test_ticket_linked_item_serializer.py | 2 +- .../test_ticket_linked_item_viewset.py | 2 +- .../manufacturer/test_manufacturer_api_v2.py | 2 +- .../test_manufacturer_permission.py | 2 +- .../test_unit_model_notes_model.py | 2 +- .../test_related_ticket_api_v2.py | 2 +- .../test_history/test_unit_history_model.py | 2 +- .../unit/test_task_result/test_task_result.py | 2 +- .../test_task_result_api_v2.py | 2 +- .../test_ticket_category_api_v2.py | 2 +- .../test_ticket_category_permission.py | 2 +- .../test_ticket_category_permission_api.py | 2 +- .../test_ticket_comment_api_v2.py | 2 +- .../test_ticket_comment_permission.py | 2 +- .../test_ticket_comment_permission_api.py | 2 +- .../test_unit_ticket_comment_model.py | 2 +- .../test_ticket_comment_category_api_v2.py | 2 +- ...test_ticket_comment_category_permission.py | 2 +- ..._ticket_comment_category_permission_api.py | 2 +- .../test_ticket_depreciated_permission.py | 2 +- .../test_ticket_depreciated_permission_api.py | 2 +- .../test_ticket_linked_item_api_v2.py | 2 +- .../test_ticket_linked_item_permission.py | 2 +- .../test_unit_ticket_linked_item_model.py | 2 +- app/core/viewsets/ticket_depreciated.py | 2 +- ...012_alter_checkin_organization_and_more.py | 41 +++++ app/devops/serializers/feature_flag.py | 4 +- app/devops/serializers/git_group.py | 4 +- app/devops/serializers/git_repository/base.py | 4 +- .../serializers/git_repository/gitlab.py | 2 +- .../software_enable_feature_flag.py | 4 +- .../test_functional_check_ins_create.py | 2 +- ...test_functional_feature_flag_serializer.py | 2 +- .../test_functional_feature_flag_viewset.py | 2 +- ...nctional_public_feature_flag_serializer.py | 2 +- ..._functional_public_feature_flag_viewset.py | 2 +- ...software_enable_feature_flag_serializer.py | 2 +- ...al_software_enable_feature_flag_viewset.py | 2 +- .../check_ins/test_unit_check_ins_model.py | 2 +- .../test_unit_feature_flag_api_v2.py | 2 +- .../test_unit_feature_flag_model.py | 2 +- .../test_unit_feature_flag_notes_model.py | 2 +- .../test_unit_public_feature_flag_api_v2.py | 2 +- ...nit_software_enable_feature_flag_api_v2.py | 2 +- ...unit_software_enable_feature_flag_model.py | 2 +- .../serializers/entity_employee.py | 4 +- app/itam/forms/software/update.py | 4 +- ...0012_alter_device_organization_and_more.py | 66 +++++++ app/itam/models/software.py | 8 +- app/itam/serializers/asset_it_asset.py | 4 +- app/itam/serializers/device.py | 4 +- app/itam/serializers/device_model.py | 4 +- app/itam/serializers/device_notes.py | 2 +- .../serializers/device_operating_system.py | 4 +- app/itam/serializers/device_software.py | 4 +- app/itam/serializers/device_type.py | 4 +- app/itam/serializers/operating_system.py | 4 +- .../serializers/operating_system_version.py | 4 +- app/itam/serializers/software.py | 4 +- app/itam/serializers/software_category.py | 4 +- app/itam/serializers/software_version.py | 4 +- app/itam/tasks/inventory.py | 2 +- .../device/test_device_serializer.py | 2 +- .../functional/device/test_device_viewset.py | 2 +- .../test_device_model_serializer.py | 2 +- .../device_model/test_device_model_viewset.py | 2 +- ...test_device_operating_system_serializer.py | 2 +- .../test_device_operating_system_viewset.py | 2 +- .../test_device_software_serializer.py | 2 +- .../test_device_software_viewset.py | 2 +- .../test_device_type_serializer.py | 2 +- .../device_type/test_device_type_viewset.py | 2 +- .../test_operating_system_installs_viewset.py | 2 +- .../test_software_installs_serializer.py | 2 +- .../test_software_installs_viewset.py | 2 +- .../inventory/test_inventory_serializer.py | 2 +- .../inventory/test_inventory_viewset.py | 2 +- .../test_operating_system_serializer.py | 2 +- .../test_operating_system_viewset.py | 2 +- ...est_operating_system_version_serializer.py | 2 +- .../test_operating_system_version_viewset.py | 2 +- .../software/test_software_serializer.py | 2 +- .../software/test_software_viewset.py | 2 +- .../test_software_category_serializer.py | 2 +- .../test_software_category_viewset.py | 2 +- .../test_software_version_serializer.py | 2 +- .../test_software_version_viewset.py | 2 +- app/itam/tests/unit/device/test_device_api.py | 2 +- .../tests/unit/device/test_device_api_v2.py | 2 +- .../unit/device/test_device_permission.py | 2 +- .../unit/device/test_device_permission_api.py | 2 +- .../device_model/test_device_model_api_v2.py | 2 +- .../test_device_model_permission.py | 2 +- .../test_device_operating_system_api_v2.py | 2 +- ...test_unit_device_operating_system_model.py | 2 +- .../test_device_software_api_v2.py | 2 +- .../test_unit_device_software_model.py | 2 +- .../device_type/test_device_type_api_v2.py | 2 +- .../test_device_type_permission.py | 2 +- .../test_operating_system_installs_api_v2.py | 2 +- .../test_software_installs_api_v2.py | 2 +- .../unit/inventory/test_api_inventory.py | 2 +- .../test_inventory_permission_api.py | 2 +- .../test_operating_system_api_v2.py | 2 +- .../test_operating_system_permission.py | 2 +- .../test_operating_system_version_api_v2.py | 2 +- ...est_unit_operating_system_version_model.py | 2 +- ...it_operating_system_version_notes_model.py | 2 +- .../tests/unit/software/test_software_api.py | 2 +- .../unit/software/test_software_api_v2.py | 2 +- .../unit/software/test_software_permission.py | 2 +- .../software/test_software_permission_api.py | 2 +- .../test_software_category_api_v2.py | 2 +- .../test_software_category_permission.py | 2 +- .../test_software_version_api_v2.py | 2 +- .../test_unit_software_version_model.py | 2 +- .../test_unit_software_version_notes_model.py | 2 +- app/itam/views/device.py | 2 +- ...010_alter_cluster_organization_and_more.py | 36 ++++ app/itim/serializers/cluster.py | 4 +- app/itim/serializers/cluster_type.py | 4 +- app/itim/serializers/port.py | 4 +- app/itim/serializers/service.py | 4 +- .../cluster/test_cluster_serializer.py | 2 +- .../cluster/test_cluster_viewset.py | 2 +- .../test_cluster_type_serializer.py | 2 +- .../test_cluster_type_viewset.py | 2 +- .../functional/port/test_port_serializer.py | 2 +- .../functional/port/test_port_viewset.py | 2 +- .../service/test_service_serializer.py | 2 +- .../service/test_service_viewset.py | 2 +- .../test_service_cluster_viewset.py | 2 +- .../test_service_device_viewset.py | 2 +- .../tests/unit/cluster/test_cluster_api_v2.py | 2 +- .../unit/cluster/test_cluster_permission.py | 2 +- .../cluster_types/test_cluster_type_api_v2.py | 2 +- .../test_cluster_type_permission.py | 2 +- app/itim/tests/unit/port/test_port_api_v2.py | 2 +- .../tests/unit/port/test_port_permission.py | 2 +- .../port_notes/test_unit_port_notes_model.py | 2 +- .../tests/unit/service/test_service_api_v2.py | 2 +- .../unit/service/test_service_permission.py | 2 +- ...006_alter_project_organization_and_more.py | 36 ++++ app/project_management/serializers/project.py | 4 +- .../serializers/project_milestone.py | 4 +- .../serializers/project_states.py | 4 +- .../serializers/project_type.py | 4 +- .../project/test_project_serializer.py | 2 +- .../project/test_project_viewset.py | 2 +- .../test_project_milestone_serializer.py | 2 +- .../test_project_milestone_viewset.py | 2 +- .../test_project_state_serializer.py | 2 +- .../test_project_state_viewset.py | 2 +- .../test_project_type_serializer.py | 2 +- .../project_type/test_project_type_viewset.py | 2 +- .../tests/unit/project/test_project_api_v2.py | 2 +- .../unit/project/test_project_permission.py | 2 +- .../project/test_project_permission_api.py | 2 +- .../test_project_milestone_api_v2.py | 2 +- .../test_project_milestone_permission.py | 2 +- .../test_project_milestone_permission_api.py | 2 +- .../test_unit_projectmilestone_model.py | 2 +- ...test_unit_project_milestone_notes_model.py | 2 +- .../test_project_state_api_v2.py | 2 +- .../test_project_state_permission.py | 2 +- .../test_project_state_permission_api.py | 2 +- .../project_type/test_project_type_api_v2.py | 2 +- .../test_project_type_permission.py | 2 +- .../test_project_type_permission_api.py | 2 +- app/settings/forms/admin_settings_global.py | 2 +- app/settings/forms/app_settings.py | 2 +- app/settings/forms/external_links.py | 2 +- app/settings/forms/user_settings.py | 2 +- ...ppsettings_global_organization_and_more.py | 36 ++++ app/settings/models/app_settings.py | 12 +- app/settings/models/user_settings.py | 8 +- app/settings/serializers/app_settings.py | 4 +- app/settings/serializers/external_links.py | 4 +- app/settings/serializers/user_settings.py | 4 +- .../app_settings/test_app_settings_viewset.py | 2 +- .../test_external_link_viewset.py | 2 +- .../test_external_links_serializer.py | 2 +- .../test_user_settings_viewset.py | 2 +- .../app_settings/test_app_settings_api_v2.py | 2 +- .../test_app_settings_permission.py | 2 +- .../test_external_links_api_v2.py | 2 +- .../test_external_links_permission.py | 2 +- .../test_unit_user_settings_model.py | 2 +- .../test_user_settings_api_v2.py | 2 +- .../test_user_settings_permissions.py | 2 +- docs/projects/centurion_erp/index.md | 2 +- .../centurion_erp/user/access/index.md | 12 +- .../centurion_erp/user/access/organization.md | 19 --- .../centurion_erp/user/access/team.md | 4 +- .../centurion_erp/user/access/tenant.md | 19 +++ .../centurion_erp/user/itim/cluster.md | 2 +- mkdocs.yml | 4 +- 348 files changed, 1107 insertions(+), 651 deletions(-) create mode 100644 app/access/migrations/0007_rename_organization_tenant.py create mode 100644 app/access/migrations/0008_alter_tenant_options_alter_entity_organization_and_more.py create mode 100644 app/access/models/tenant.py create mode 100644 app/accounting/migrations/0004_alter_assetbase_organization.py create mode 100644 app/assistance/migrations/0006_alter_knowledgebase_organization_and_more.py create mode 100644 app/config_management/migrations/0008_alter_configgrouphosts_organization_and_more.py create mode 100644 app/core/migrations/0026_alter_manufacturer_organization_and_more.py create mode 100644 app/devops/migrations/0012_alter_checkin_organization_and_more.py create mode 100644 app/itam/migrations/0012_alter_device_organization_and_more.py create mode 100644 app/itim/migrations/0010_alter_cluster_organization_and_more.py create mode 100644 app/project_management/migrations/0006_alter_project_organization_and_more.py create mode 100644 app/settings/migrations/0012_alter_appsettings_global_organization_and_more.py delete mode 100644 docs/projects/centurion_erp/user/access/organization.md create mode 100644 docs/projects/centurion_erp/user/access/tenant.md diff --git a/.gitignore b/.gitignore index 201ad60b..a7265761 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ venv/** */static/** __pycache__ -**.sqlite3 +**.sqlite* **.sqlite **.coverage .coverage* diff --git a/Release-Notes.md b/Release-Notes.md index 88c08fd9..e30776a1 100644 --- a/Release-Notes.md +++ b/Release-Notes.md @@ -16,6 +16,10 @@ With this new setting, the previous setting `LOGGING` will no longer function. +- Renamed `Organization` model to `Tenant` so as to reflect what is actually is. + +- `robots.txt` file now being served from the API container at path `/robots.txt` with `User-agent: *` and `Disallow: /` + ## Version 1.16.0 diff --git a/app/access/admin.py b/app/access/admin.py index a519b93c..b0a55617 100644 --- a/app/access/admin.py +++ b/app/access/admin.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/forms/organization.py b/app/access/forms/organization.py index 94678d84..2cb0412e 100644 --- a/app/access/forms/organization.py +++ b/app/access/forms/organization.py @@ -3,7 +3,7 @@ from django.db.models import Q from app import settings -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.forms.common import CommonModelForm diff --git a/app/access/middleware/request.py b/app/access/middleware/request.py index 460234d8..c609fb74 100644 --- a/app/access/middleware/request.py +++ b/app/access/middleware/request.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import Group from django.utils.deprecation import MiddlewareMixin -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team diff --git a/app/access/migrations/0007_rename_organization_tenant.py b/app/access/migrations/0007_rename_organization_tenant.py new file mode 100644 index 00000000..284afd96 --- /dev/null +++ b/app/access/migrations/0007_rename_organization_tenant.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1.9 on 2025-05-14 11:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0005_entity_person_entityhistory_entitynotes_role_and_more'), + ('accounting', '0003_assetbasenotes'), + ('assistance', '0005_knowledgebasecategoryhistory_knowledgebasehistory'), + ('config_management', '0007_configgroupshistory_configgrouphostshistory_and_more'), + ('core', '0025_ticketcommentaction'), + ('devops', '0011_alter_gitgroup_unique_together_and_more'), + ('itam', '0011_itamassetbase'), + ('itim', '0009_slmticket_requestticket'), + ('project_management', '0005_projecthistory_projectmilestonehistory_and_more'), + ('settings', '0011_appsettingshistory_externallinkhistory'), + ] + + operations = [ + migrations.RenameModel( + old_name = 'Organization', + new_name = 'Tenant' + ), + ] diff --git a/app/access/migrations/0008_alter_tenant_options_alter_entity_organization_and_more.py b/app/access/migrations/0008_alter_tenant_options_alter_entity_organization_and_more.py new file mode 100644 index 00000000..22e36bdb --- /dev/null +++ b/app/access/migrations/0008_alter_tenant_options_alter_entity_organization_and_more.py @@ -0,0 +1,47 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.team +import access.models.tenancy +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0007_rename_organization_tenant'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterModelOptions( + name='tenant', + options={'ordering': ['name'], 'verbose_name': 'Tenant', 'verbose_name_plural': 'Tenants'}, + ), + migrations.AlterField( + model_name='entity', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='role', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='team', + name='organization', + field=models.ForeignKey(help_text='Tenant this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.tenant', validators=[access.models.team.Team.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='tenant', + name='manager', + field=models.ForeignKey(help_text='Manager for this Tenancy', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Manager'), + ), + migrations.AlterField( + model_name='tenant', + name='name', + field=models.CharField(help_text='Name of this Tenancy', max_length=50, unique=True, verbose_name='Name'), + ), + ] diff --git a/app/access/mixin.py b/app/access/mixin.py index dddf1714..4bebe8f8 100644 --- a/app/access/mixin.py +++ b/app/access/mixin.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Group from django.core.exceptions import PermissionDenied from django.utils.functional import cached_property -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team @@ -260,7 +260,7 @@ class OrganizationMixin(): self.permission_required = permissions_required organization_manager_models = [ - 'access.organization', + 'access.tenant', 'access.team', 'access.teamusers', ] @@ -326,7 +326,7 @@ class OrganizationMixin(): if required_permission.replace( 'view_', '' - ) == 'access.organization' and len(self.kwargs) == 0: + ) == 'access.tenant' and len(self.kwargs) == 0: return True diff --git a/app/access/mixins/organization.py b/app/access/mixins/organization.py index a90103a6..1d318f81 100644 --- a/app/access/mixins/organization.py +++ b/app/access/mixins/organization.py @@ -3,7 +3,7 @@ import django from django.contrib.auth.models import Group from django.db import models -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team User = django.contrib.auth.get_user_model() @@ -93,7 +93,7 @@ class OrganizationMixin: self._obj_organization = obj.organization - elif str(self.model._meta.verbose_name).lower() == 'organization': + elif str(self.model._meta.verbose_name).lower() == 'tenant': self._obj_organization = obj diff --git a/app/access/mixins/permissions.py b/app/access/mixins/permissions.py index c64808d6..9b9d31f9 100644 --- a/app/access/mixins/permissions.py +++ b/app/access/mixins/permissions.py @@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist from rest_framework import exceptions from rest_framework.permissions import DjangoObjectPermissions -from access.models.tenancy import Organization, TenancyObject +from access.models.tenancy import Tenant, TenancyObject from core import exceptions as centurion_exceptions @@ -14,10 +14,10 @@ from core import exceptions as centurion_exceptions class OrganizationPermissionMixin( DjangoObjectPermissions, ): - """Organization Permission Mixin + """Tenant Permission Mixin This class is to be used as the permission class for API `Views`/`ViewSets`. - In combination with the `OrganizationPermissionsMixin`, permission checking + In combination with the `TenantPermissionsMixin`, permission checking will be done to ensure the user has the correct permissions to perform the CRUD operation. @@ -166,7 +166,7 @@ class OrganizationPermissionMixin( raise centurion_exceptions.PermissionDenied() - obj_organization: Organization = view.get_obj_organization( + obj_organization: Tenant = view.get_obj_organization( request = request ) diff --git a/app/access/models/organization.py b/app/access/models/organization.py index b7561019..c7f5047a 100644 --- a/app/access/models/organization.py +++ b/app/access/models/organization.py @@ -1,159 +1 @@ -import django - -from django.conf import settings -from django.db import models - -from rest_framework.reverse import reverse - -from access.fields import ( - AutoCreatedField, - AutoLastModifiedField, - AutoSlugField -) - -from core.mixin.history_save import SaveHistory - -User = django.contrib.auth.get_user_model() - - - -class Organization(SaveHistory): - - class Meta: - verbose_name = "Organization" - verbose_name_plural = "Organizations" - ordering = ['name'] - - def save(self, *args, **kwargs): - - if self.slug == '_': - self.slug = self.name.lower().replace(' ', '_') - - super().save(*args, **kwargs) - - id = models.AutoField( - blank=False, - help_text = 'ID of this item', - primary_key=True, - unique=True, - verbose_name = 'ID' - ) - - name = models.CharField( - blank = False, - help_text = 'Name of this Organization', - max_length = 50, - unique = True, - verbose_name = 'Name' - ) - - manager = models.ForeignKey( - settings.AUTH_USER_MODEL, - blank = False, - help_text = 'Manager for this organization', - null = True, - on_delete=models.SET_NULL, - verbose_name = 'Manager' - ) - - model_notes = models.TextField( - blank = True, - default = None, - help_text = 'Tid bits of information', - null= True, - verbose_name = 'Notes', - ) - - slug = AutoSlugField() - - created = AutoCreatedField() - - modified = AutoLastModifiedField() - - - def get_organization(self): - return self - - def __int__(self): - - return self.id - - def __str__(self): - return self.name - - table_fields: list = [ - 'nbsp', - 'name', - 'created', - 'modified', - 'nbsp' - ] - - page_layout: list = [ - { - "name": "Details", - "slug": "details", - "sections": [ - { - "layout": "double", - "left": [ - 'name', - 'manager', - 'created', - 'modified', - ], - "right": [ - 'model_notes', - ] - } - ] - }, - { - "name": "Teams", - "slug": "teams", - "sections": [ - { - "layout": "table", - "field": "teams" - } - ] - }, - { - "name": "Knowledge Base", - "slug": "kb_articles", - "sections": [ - { - "layout": "table", - "field": "knowledge_base", - } - ] - }, - { - "name": "Notes", - "slug": "notes", - "sections": [] - } - ] - - - def get_url( self, request = None ) -> str: - - if request: - - return reverse("v2:_api_v2_organization-detail", request=request, kwargs={'pk': self.id}) - - return reverse("v2:_api_v2_organization-detail", kwargs={'pk': self.id}) - - - def save_history(self, before: dict, after: dict) -> bool: - - from access.models.organization_history import OrganizationHistory - - history = super().save_history( - before = before, - after = after, - history_model = OrganizationHistory - ) - - - return history +from .tenant import Tenant as Organization diff --git a/app/access/models/organization_history.py b/app/access/models/organization_history.py index cd688f8e..e9603a29 100644 --- a/app/access/models/organization_history.py +++ b/app/access/models/organization_history.py @@ -2,7 +2,7 @@ from django.db import models from core.models.model_history import ModelHistory -from access.models.organization import Organization +from access.models.tenant import Tenant @@ -23,7 +23,7 @@ class OrganizationHistory( model = models.ForeignKey( - Organization, + Tenant, blank = False, help_text = 'Model this note belongs to', null = False, @@ -46,8 +46,8 @@ class OrganizationHistory( model = None - from access.serializers.organization import OrganizationBaseSerializer + from access.serializers.organization import TenantBaseSerializer - model = OrganizationBaseSerializer(self.model, context = serializer_context) + model = TenantBaseSerializer(self.model, context = serializer_context) return model diff --git a/app/access/models/organization_notes.py b/app/access/models/organization_notes.py index 6a495239..468a0141 100644 --- a/app/access/models/organization_notes.py +++ b/app/access/models/organization_notes.py @@ -1,6 +1,6 @@ from django.db import models -from access.models.organization import Organization +from access.models.tenant import Tenant from core.models.model_notes import ModelNotes @@ -23,7 +23,7 @@ class OrganizationNotes( model = models.ForeignKey( - Organization, + Tenant, blank = False, help_text = 'Model this note belongs to', null = False, diff --git a/app/access/models/team.py b/app/access/models/team.py index adfef107..25142abd 100644 --- a/app/access/models/team.py +++ b/app/access/models/team.py @@ -8,7 +8,7 @@ from access.fields import ( AutoLastModifiedField ) -from access.models.organization import Organization +from access.models.tenant import Tenant from access.models.tenancy import TenancyObject from core import exceptions as centurion_exceptions @@ -55,13 +55,13 @@ class Team(Group, TenancyObject): ) organization = models.ForeignKey( - Organization, + Tenant, blank = False, - help_text = 'Organization this belongs to', + help_text = 'Tenant this belongs to', null = False, on_delete = models.CASCADE, validators = [validatate_organization_exists], - verbose_name = 'Organization' + verbose_name = 'Tenant' ) created = AutoCreatedField() diff --git a/app/access/models/team_user.py b/app/access/models/team_user.py index d8aa0fdc..aacb213b 100644 --- a/app/access/models/team_user.py +++ b/app/access/models/team_user.py @@ -11,7 +11,7 @@ from access.fields import ( AutoLastModifiedField ) -from access.models.organization import Organization +from access.models.tenant import Tenant from access.models.team import Team from core.lib.feature_not_used import FeatureNotUsed @@ -99,7 +99,7 @@ class TeamUsers(SaveHistory): user.groups.remove(group) - def get_organization(self) -> Organization: + def get_organization(self) -> Tenant: return self.team.organization diff --git a/app/access/models/tenancy.py b/app/access/models/tenancy.py index a44dc2d8..d8f28704 100644 --- a/app/access/models/tenancy.py +++ b/app/access/models/tenancy.py @@ -5,7 +5,7 @@ from django.db import models from rest_framework.reverse import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant from core import exceptions as centurion_exceptions from core.middleware.get_request import get_request @@ -136,14 +136,14 @@ class TenancyObject(SaveHistory): ) organization = models.ForeignKey( - Organization, + Tenant, blank = False, - help_text = 'Organization this belongs to', + help_text = 'Tenancy this belongs to', null = False, on_delete = models.CASCADE, related_name = '+', validators = [validatate_organization_exists], - verbose_name = 'Organization' + verbose_name = 'Tenant' ) is_global = models.BooleanField( @@ -161,7 +161,7 @@ class TenancyObject(SaveHistory): verbose_name = 'Notes', ) - def get_organization(self) -> Organization: + def get_organization(self) -> Tenant: return self.organization app_namespace: str = None @@ -291,7 +291,7 @@ class TenancyObject(SaveHistory): raise centurion_exceptions.ValidationError( detail = { - 'organization': 'Organization is required' + 'organization': 'Tenant is required' }, code = 'required' ) diff --git a/app/access/models/tenant.py b/app/access/models/tenant.py new file mode 100644 index 00000000..dd625d39 --- /dev/null +++ b/app/access/models/tenant.py @@ -0,0 +1,161 @@ +import django + +from django.conf import settings +from django.db import models + +from rest_framework.reverse import reverse + +from access.fields import ( + AutoCreatedField, + AutoLastModifiedField, + AutoSlugField +) + +from core.mixin.history_save import SaveHistory + +User = django.contrib.auth.get_user_model() + +class Tenant(SaveHistory): + + class Meta: + verbose_name = "Tenant" + verbose_name_plural = "Tenants" + ordering = ['name'] + + def save(self, *args, **kwargs): + + if self.slug == '_': + self.slug = self.name.lower().replace(' ', '_') + + super().save(*args, **kwargs) + + id = models.AutoField( + blank=False, + help_text = 'ID of this item', + primary_key=True, + unique=True, + verbose_name = 'ID' + ) + + name = models.CharField( + blank = False, + help_text = 'Name of this Tenancy', + max_length = 50, + unique = True, + verbose_name = 'Name' + ) + + manager = models.ForeignKey( + settings.AUTH_USER_MODEL, + blank = False, + help_text = 'Manager for this Tenancy', + null = True, + on_delete=models.SET_NULL, + verbose_name = 'Manager' + ) + + model_notes = models.TextField( + blank = True, + default = None, + help_text = 'Tid bits of information', + null= True, + verbose_name = 'Notes', + ) + + slug = AutoSlugField() + + created = AutoCreatedField() + + modified = AutoLastModifiedField() + + + def get_organization(self): + return self + + def __int__(self): + + return self.id + + def __str__(self): + return self.name + + table_fields: list = [ + 'nbsp', + 'name', + 'created', + 'modified', + 'nbsp' + ] + + page_layout: list = [ + { + "name": "Details", + "slug": "details", + "sections": [ + { + "layout": "double", + "left": [ + 'name', + 'manager', + 'created', + 'modified', + ], + "right": [ + 'model_notes', + ] + } + ] + }, + { + "name": "Teams", + "slug": "teams", + "sections": [ + { + "layout": "table", + "field": "teams" + } + ] + }, + { + "name": "Knowledge Base", + "slug": "kb_articles", + "sections": [ + { + "layout": "table", + "field": "knowledge_base", + } + ] + }, + { + "name": "Notes", + "slug": "notes", + "sections": [] + } + ] + + + def get_url( self, request = None ) -> str: + + if request: + + return reverse("v2:_api_v2_organization-detail", request=request, kwargs={'pk': self.id}) + + return reverse("v2:_api_v2_organization-detail", kwargs={'pk': self.id}) + + + def save_history(self, before: dict, after: dict) -> bool: + + from access.models.organization_history import OrganizationHistory + + history = super().save_history( + before = before, + after = after, + history_model = OrganizationHistory + ) + + + return history + + + +Organization = Tenant diff --git a/app/access/serializers/entity.py b/app/access/serializers/entity.py index 0e5cbb65..336355e1 100644 --- a/app/access/serializers/entity.py +++ b/app/access/serializers/entity.py @@ -6,7 +6,7 @@ from access.models.entity import Entity from api.serializers import common -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer @@ -87,4 +87,4 @@ class ModelSerializer( class ViewSerializer(ModelSerializer): """Entity Base View Model""" - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/access/serializers/entity_contact.py b/app/access/serializers/entity_contact.py index 8cb72ae5..3343ab16 100644 --- a/app/access/serializers/entity_contact.py +++ b/app/access/serializers/entity_contact.py @@ -6,7 +6,7 @@ from access.serializers.entity_person import ( BaseSerializer as BaseBaseSerializer, ModelSerializer as BaseModelSerializer, ) -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer @@ -72,4 +72,4 @@ class ViewSerializer( This model inherits from the Person model. """ - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/access/serializers/entity_person.py b/app/access/serializers/entity_person.py index 1ad706b4..6b5b2489 100644 --- a/app/access/serializers/entity_person.py +++ b/app/access/serializers/entity_person.py @@ -6,7 +6,7 @@ from access.serializers.entity import ( BaseSerializer as BaseBaseSerializer, ModelSerializer as BaseModelSerializer, ) -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer @@ -70,4 +70,4 @@ class ViewSerializer( This model inherits from the Entity base model. """ - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/access/serializers/organization.py b/app/access/serializers/organization.py index 993d76c8..7645b947 100644 --- a/app/access/serializers/organization.py +++ b/app/access/serializers/organization.py @@ -2,15 +2,16 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.models.organization import Organization +from access.models.tenant import Tenant from app.serializers.user import UserBaseSerializer from core import fields as centurion_field +Organization = Tenant -class OrganizationBaseSerializer(serializers.ModelSerializer): +class TenantBaseSerializer(serializers.ModelSerializer): display_name = serializers.SerializerMethodField('get_display_name') @@ -24,7 +25,7 @@ class OrganizationBaseSerializer(serializers.ModelSerializer): class Meta: - model = Organization + model = Tenant fields = [ 'id', @@ -42,8 +43,8 @@ class OrganizationBaseSerializer(serializers.ModelSerializer): -class OrganizationModelSerializer( - OrganizationBaseSerializer +class TenantModelSerializer( + TenantBaseSerializer ): _urls = serializers.SerializerMethodField('get_url') @@ -74,7 +75,7 @@ class OrganizationModelSerializer( class Meta: - model = Organization + model = Tenant fields = '__all__' @@ -98,7 +99,7 @@ class OrganizationModelSerializer( ] -class OrganizationViewSerializer(OrganizationModelSerializer): +class TenantViewSerializer(TenantModelSerializer): pass manager = UserBaseSerializer(many=False, read_only = True) diff --git a/app/access/serializers/role.py b/app/access/serializers/role.py index 1012f949..597334ad 100644 --- a/app/access/serializers/role.py +++ b/app/access/serializers/role.py @@ -5,7 +5,7 @@ from drf_spectacular.utils import extend_schema_serializer from access.functions.permissions import permission_queryset from access.models.role import Role -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -109,6 +109,6 @@ class ModelSerializer( class ViewSerializer(ModelSerializer): """Role Base View Model""" - organization = OrganizationBaseSerializer( many=False, read_only=True ) + organization = TenantBaseSerializer( many=False, read_only=True ) permissions = PermissionBaseSerializer( many=True, read_only=True ) diff --git a/app/access/serializers/teams.py b/app/access/serializers/teams.py index c32185b2..d677a9c3 100644 --- a/app/access/serializers/teams.py +++ b/app/access/serializers/teams.py @@ -7,7 +7,7 @@ from access.models.team import Team from api.serializers import common from access.functions.permissions import permission_queryset -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from app.serializers.permission import Permission, PermissionBaseSerializer @@ -127,6 +127,6 @@ class TeamModelSerializer( class TeamViewSerializer(TeamModelSerializer): - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) permissions = PermissionBaseSerializer(many = True) diff --git a/app/access/tests/functional/entity/test_functional_entity_serializer.py b/app/access/tests/functional/entity/test_functional_entity_serializer.py index 6d84fcf7..6b6e8c01 100644 --- a/app/access/tests/functional/entity/test_functional_entity_serializer.py +++ b/app/access/tests/functional/entity/test_functional_entity_serializer.py @@ -2,7 +2,7 @@ import pytest from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.serializers.entity import ( Entity, ModelSerializer diff --git a/app/access/tests/functional/entity/test_functional_entity_viewset.py b/app/access/tests/functional/entity/test_functional_entity_viewset.py index 7d1d4720..e0445551 100644 --- a/app/access/tests/functional/entity/test_functional_entity_viewset.py +++ b/app/access/tests/functional/entity/test_functional_entity_viewset.py @@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType from django.test import TestCase from access.models.entity import Entity -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/functional/organization/test_organization_history.py b/app/access/tests/functional/organization/test_organization_history.py index 1bcf6978..e562285d 100644 --- a/app/access/tests/functional/organization/test_organization_history.py +++ b/app/access/tests/functional/organization/test_organization_history.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization_history import Organization, OrganizationHistory +from access.models.organization_history import Tenant as Organization, OrganizationHistory from core.tests.abstract.test_functional_history import HistoryEntriesCommon diff --git a/app/access/tests/functional/organization/test_organization_serializer.py b/app/access/tests/functional/organization/test_organization_serializer.py index d5abfcd3..88855252 100644 --- a/app/access/tests/functional/organization/test_organization_serializer.py +++ b/app/access/tests/functional/organization/test_organization_serializer.py @@ -6,8 +6,8 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError from access.serializers.organization import ( - Organization, - OrganizationModelSerializer + Tenant, + TenantModelSerializer ) User = django.contrib.auth.get_user_model() @@ -18,7 +18,7 @@ class OrganizationValidationAPI( TestCase, ): - model = Organization + model = Tenant @classmethod def setUpTestData(self): @@ -47,7 +47,7 @@ class OrganizationValidationAPI( Ensure that if creating and no name is provided a validation error occurs """ - serializer = OrganizationModelSerializer( + serializer = TenantModelSerializer( data = self.valid_data ) @@ -67,7 +67,7 @@ class OrganizationValidationAPI( with pytest.raises(ValidationError) as err: - serializer = OrganizationModelSerializer( + serializer = TenantModelSerializer( data = data ) @@ -87,7 +87,7 @@ class OrganizationValidationAPI( del data['manager'] - serializer = OrganizationModelSerializer( + serializer = TenantModelSerializer( data = data ) diff --git a/app/access/tests/functional/organization/test_organization_viewset.py b/app/access/tests/functional/organization/test_organization_viewset.py index 75575d95..1ba090cd 100644 --- a/app/access/tests/functional/organization/test_organization_viewset.py +++ b/app/access/tests/functional/organization/test_organization_viewset.py @@ -10,7 +10,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers @@ -318,4 +318,4 @@ class OrganizationMetadata( menu_id = 'access' - menu_entry_id = 'organization' \ No newline at end of file + menu_entry_id = 'tenant' \ No newline at end of file diff --git a/app/access/tests/functional/organization_notes/test_organization_notes_api_v2.py b/app/access/tests/functional/organization_notes/test_organization_notes_api_v2.py index 1153ded8..05aadcbf 100644 --- a/app/access/tests/functional/organization_notes/test_organization_notes_api_v2.py +++ b/app/access/tests/functional/organization_notes/test_organization_notes_api_v2.py @@ -3,7 +3,7 @@ from django.test import TestCase from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.organization_notes import OrganizationNotes diff --git a/app/access/tests/functional/role/test_functional_role_serializer.py b/app/access/tests/functional/role/test_functional_role_serializer.py index 73235089..62597500 100644 --- a/app/access/tests/functional/role/test_functional_role_serializer.py +++ b/app/access/tests/functional/role/test_functional_role_serializer.py @@ -5,7 +5,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.serializers.role import Role, ModelSerializer diff --git a/app/access/tests/functional/role/test_functional_role_viewset.py b/app/access/tests/functional/role/test_functional_role_viewset.py index b2eafa8c..b0b8ef18 100644 --- a/app/access/tests/functional/role/test_functional_role_viewset.py +++ b/app/access/tests/functional/role/test_functional_role_viewset.py @@ -6,7 +6,7 @@ from django.test import Client, TestCase from rest_framework.reverse import reverse from access.models.role import Role -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/functional/team/test_team_permission_viewset.py b/app/access/tests/functional/team/test_team_permission_viewset.py index ec001554..a9b52cd2 100644 --- a/app/access/tests/functional/team/test_team_permission_viewset.py +++ b/app/access/tests/functional/team/test_team_permission_viewset.py @@ -11,7 +11,7 @@ from django.test import Client, TestCase from rest_framework.reverse import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/functional/team/test_team_serializer.py b/app/access/tests/functional/team/test_team_serializer.py index 0135ced1..bbabf2bf 100644 --- a/app/access/tests/functional/team/test_team_serializer.py +++ b/app/access/tests/functional/team/test_team_serializer.py @@ -9,7 +9,7 @@ from rest_framework.exceptions import ValidationError from access.middleware.request import Tenancy -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.serializers.teams import ( Team, diff --git a/app/access/tests/functional/team_user/test_team_user_permission_viewset.py b/app/access/tests/functional/team_user/test_team_user_permission_viewset.py index 8855b9ca..5740d533 100644 --- a/app/access/tests/functional/team_user/test_team_user_permission_viewset.py +++ b/app/access/tests/functional/team_user/test_team_user_permission_viewset.py @@ -11,7 +11,7 @@ from django.test import Client, TestCase from rest_framework.reverse import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/functional/team_user/test_team_user_serializer.py b/app/access/tests/functional/team_user/test_team_user_serializer.py index 694ee624..b8412d19 100644 --- a/app/access/tests/functional/team_user/test_team_user_serializer.py +++ b/app/access/tests/functional/team_user/test_team_user_serializer.py @@ -7,7 +7,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.serializers.team_user import ( diff --git a/app/access/tests/unit/entity/test_unit_entity_api_v2.py b/app/access/tests/unit/entity/test_unit_entity_api_v2.py index c0d1310c..aee1c266 100644 --- a/app/access/tests/unit/entity/test_unit_entity_api_v2.py +++ b/app/access/tests/unit/entity/test_unit_entity_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase # from rest_framework.relations import Hyperlink from access.models.entity import Entity -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/mixin/test_permission.py b/app/access/tests/unit/mixin/test_permission.py index ce04fddf..9e4f36b2 100644 --- a/app/access/tests/unit/mixin/test_permission.py +++ b/app/access/tests/unit/mixin/test_permission.py @@ -15,7 +15,7 @@ from api.viewsets.common import ModelViewSet from access.mixins.organization import OrganizationMixin from access.mixins.permissions import OrganizationPermissionMixin -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/organization/test_organizaiton_api.py b/app/access/tests/unit/organization/test_organizaiton_api.py index 8e6b0f47..31c4a153 100644 --- a/app/access/tests/unit/organization/test_organizaiton_api.py +++ b/app/access/tests/unit/organization/test_organizaiton_api.py @@ -10,7 +10,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/organization/test_organizaiton_api_v2.py b/app/access/tests/unit/organization/test_organizaiton_api_v2.py index 73b9f36f..9ffe6e03 100644 --- a/app/access/tests/unit/organization/test_organizaiton_api_v2.py +++ b/app/access/tests/unit/organization/test_organizaiton_api_v2.py @@ -10,7 +10,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/organization/test_organizaiton_permission.py b/app/access/tests/unit/organization/test_organizaiton_permission.py index e90b71f8..989fa432 100644 --- a/app/access/tests/unit/organization/test_organizaiton_permission.py +++ b/app/access/tests/unit/organization/test_organizaiton_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant from access.models.team import Team from access.models.team_user import TeamUsers from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissionChange, OrganizationManagerModelPermissionView @@ -20,7 +20,7 @@ from app.tests.abstract.model_permissions import ModelPermissionsView, ModelPerm User = django.contrib.auth.get_user_model() -class OrganizationPermissions( +class TenantPermissions( TestCase, ModelPermissionsView, ModelPermissionsChange, @@ -28,7 +28,7 @@ class OrganizationPermissions( OrganizationManagerModelPermissionView, ): - model = Organization + model = Tenant app_namespace = 'Access' @@ -53,11 +53,11 @@ class OrganizationPermissions( 4. create a user per team """ - organization = Organization.objects.create(name='test_org') + organization = Tenant.objects.create(name='test_org') self.organization = organization - different_organization = Organization.objects.create( + different_organization = Tenant.objects.create( name='test_different_organization' ) diff --git a/app/access/tests/unit/organization/test_organizaiton_permission_api.py b/app/access/tests/unit/organization/test_organizaiton_permission_api.py index 46f2f416..4d39a833 100644 --- a/app/access/tests/unit/organization/test_organizaiton_permission_api.py +++ b/app/access/tests/unit/organization/test_organizaiton_permission_api.py @@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/organization/test_organization.py b/app/access/tests/unit/organization/test_organization.py index 00e5abe8..efb31bb8 100644 --- a/app/access/tests/unit/organization/test_organization.py +++ b/app/access/tests/unit/organization/test_organization.py @@ -7,7 +7,7 @@ import unittest from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team diff --git a/app/access/tests/unit/organization/test_unit_organization_history_api_v2.py b/app/access/tests/unit/organization/test_unit_organization_history_api_v2.py index 443722b0..3fd241df 100644 --- a/app/access/tests/unit/organization/test_unit_organization_history_api_v2.py +++ b/app/access/tests/unit/organization/test_unit_organization_history_api_v2.py @@ -3,7 +3,7 @@ from django.test import TestCase from core.tests.abstract.test_unit_model_history_api_v2 import PrimaryModelHistoryAPI -from access.models.organization_history import Organization, OrganizationHistory +from access.models.organization_history import Tenant as Organization, OrganizationHistory diff --git a/app/access/tests/unit/role/test_unit_role_api_v2.py b/app/access/tests/unit/role/test_unit_role_api_v2.py index 5faaeee0..42096a2f 100644 --- a/app/access/tests/unit/role/test_unit_role_api_v2.py +++ b/app/access/tests/unit/role/test_unit_role_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase # from rest_framework.relations import Hyperlink from access.models.role import Role -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/team/test_team_api.py b/app/access/tests/unit/team/test_team_api.py index 01ccab6b..5507e452 100644 --- a/app/access/tests/unit/team/test_team_api.py +++ b/app/access/tests/unit/team/test_team_api.py @@ -12,7 +12,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/team/test_team_api_v2.py b/app/access/tests/unit/team/test_team_api_v2.py index e57290f2..0ac6e9d8 100644 --- a/app/access/tests/unit/team/test_team_api_v2.py +++ b/app/access/tests/unit/team/test_team_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/team/test_team_permission.py b/app/access/tests/unit/team/test_team_permission.py index d1dfe840..f2f84c12 100644 --- a/app/access/tests/unit/team/test_team_permission.py +++ b/app/access/tests/unit/team/test_team_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissions diff --git a/app/access/tests/unit/team/test_team_permission_api.py b/app/access/tests/unit/team/test_team_permission_api.py index 17d32d8e..dc7b205e 100644 --- a/app/access/tests/unit/team/test_team_permission_api.py +++ b/app/access/tests/unit/team/test_team_permission_api.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/team_user/test_team_user_api_v2.py b/app/access/tests/unit/team_user/test_team_user_api_v2.py index 20fc8f57..8d0d0fa8 100644 --- a/app/access/tests/unit/team_user/test_team_user_api_v2.py +++ b/app/access/tests/unit/team_user/test_team_user_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/team_user/test_team_user_functions.py b/app/access/tests/unit/team_user/test_team_user_functions.py index 8f40edab..7ff3064f 100644 --- a/app/access/tests/unit/team_user/test_team_user_functions.py +++ b/app/access/tests/unit/team_user/test_team_user_functions.py @@ -7,7 +7,7 @@ import unittest from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team diff --git a/app/access/tests/unit/team_user/test_team_user_permission.py b/app/access/tests/unit/team_user/test_team_user_permission.py index 7478722b..c80af376 100644 --- a/app/access/tests/unit/team_user/test_team_user_permission.py +++ b/app/access/tests/unit/team_user/test_team_user_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/team_user/test_unit_team_user_model.py b/app/access/tests/unit/team_user/test_unit_team_user_model.py index 4ea8011a..213e0bf7 100644 --- a/app/access/tests/unit/team_user/test_unit_team_user_model.py +++ b/app/access/tests/unit/team_user/test_unit_team_user_model.py @@ -1,7 +1,7 @@ import django from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/tests/unit/tenancy_object/test_unit_tenancy_object_model.py b/app/access/tests/unit/tenancy_object/test_unit_tenancy_object_model.py index 86c062ff..ad83a17e 100644 --- a/app/access/tests/unit/tenancy_object/test_unit_tenancy_object_model.py +++ b/app/access/tests/unit/tenancy_object/test_unit_tenancy_object_model.py @@ -7,7 +7,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.test import TestCase from django.urls.exceptions import NoReverseMatch -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.tenancy import TenancyManager from access.models.tenancy import ( TenancyObject, diff --git a/app/access/views/organization.py b/app/access/views/organization.py index c17cec12..b19b2129 100644 --- a/app/access/views/organization.py +++ b/app/access/views/organization.py @@ -4,7 +4,7 @@ from django.utils.decorators import method_decorator from django.views import generic from access.mixin import * -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.forms.organization import OrganizationForm @@ -15,7 +15,7 @@ class IndexView(IndexView): model = Organization permission_required = [ - 'access.view_organization' + 'access.view_tenant' ] template_name = 'access/index.html.j2' context_object_name = "organization_list" @@ -61,7 +61,7 @@ class View(ChangeView): return self.handle_no_permission() - if not self.permission_check(request, [ 'access.view_organization' ]): + if not self.permission_check(request, [ 'access.view_tenant' ]): raise PermissionDenied('You are not part of this organization') @@ -97,7 +97,7 @@ class View(ChangeView): return self.handle_no_permission() - if not self.permission_check(request, [ 'access.change_organization' ]): + if not self.permission_check(request, [ 'access.change_tenant' ]): raise PermissionDenied('You are not part of this organization') diff --git a/app/access/views/team.py b/app/access/views/team.py index 234c19df..f8914ba7 100644 --- a/app/access/views/team.py +++ b/app/access/views/team.py @@ -5,7 +5,7 @@ from django.urls import reverse from access.forms.team import TeamForm, TeamFormAdd from access.mixin import * -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/access/viewsets/organization.py b/app/access/viewsets/organization.py index ffd14105..b74a4286 100644 --- a/app/access/viewsets/organization.py +++ b/app/access/viewsets/organization.py @@ -3,9 +3,9 @@ from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResp # THis import only exists so that the migrations can be created from access.models.organization_history import OrganizationHistory # pylint: disable=W0611:unused-import from access.serializers.organization import ( - Organization, - OrganizationModelSerializer, - OrganizationViewSerializer + Tenant, + TenantModelSerializer, + TenantViewSerializer ) from api.viewsets.common import ModelViewSet @@ -19,7 +19,7 @@ from api.viewsets.common import ModelViewSet description='', responses = { # 200: OpenApiResponse(description='Allready exists', response=OrganizationViewSerializer), - 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer), + 201: OpenApiResponse(description='Created', response=TenantViewSerializer), # 400: OpenApiResponse(description='Validation failed.'), 403: OpenApiResponse(description='User is missing add permissions'), } @@ -36,7 +36,7 @@ from api.viewsets.common import ModelViewSet summary = 'Fetch all orgnaizations', description='', responses = { - 200: OpenApiResponse(description='', response=OrganizationViewSerializer), + 200: OpenApiResponse(description='', response=TenantViewSerializer), 403: OpenApiResponse(description='User is missing view permissions'), } ), @@ -44,7 +44,7 @@ from api.viewsets.common import ModelViewSet summary = 'Fetch a single orgnaization', description='', responses = { - 200: OpenApiResponse(description='', response=OrganizationViewSerializer), + 200: OpenApiResponse(description='', response=TenantViewSerializer), 403: OpenApiResponse(description='User is missing view permissions'), } ), @@ -53,7 +53,7 @@ from api.viewsets.common import ModelViewSet summary = 'Update an orgnaization', description = '', responses = { - 200: OpenApiResponse(description='', response=OrganizationViewSerializer), + 200: OpenApiResponse(description='', response=TenantViewSerializer), # 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer), # # 400: OpenApiResponse(description='Validation failed.'), 403: OpenApiResponse(description='User is missing change permissions'), @@ -71,9 +71,9 @@ class ViewSet( ModelViewSet ): 'name', ] - model = Organization + model = Tenant - view_description = 'Centurion Organizations' + view_description = 'Centurion Tenants' def get_serializer_class(self): diff --git a/app/access/viewsets/team.py b/app/access/viewsets/team.py index ce0e7096..07b42b56 100644 --- a/app/access/viewsets/team.py +++ b/app/access/viewsets/team.py @@ -1,6 +1,6 @@ from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization # THis import only exists so that the migrations can be created from access.models.team_history import TeamHistory # pylint: disable=W0611:unused-import from access.serializers.teams import ( diff --git a/app/accounting/migrations/0004_alter_assetbase_organization.py b/app/accounting/migrations/0004_alter_assetbase_organization.py new file mode 100644 index 00000000..9c9154bd --- /dev/null +++ b/app/accounting/migrations/0004_alter_assetbase_organization.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('accounting', '0003_assetbasenotes'), + ] + + operations = [ + migrations.AlterField( + model_name='assetbase', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/accounting/serializers/asset.py b/app/accounting/serializers/asset.py index 96b6c9e9..2261c17e 100644 --- a/app/accounting/serializers/asset.py +++ b/app/accounting/serializers/asset.py @@ -2,7 +2,7 @@ from rest_framework import serializers from drf_spectacular.utils import extend_schema_serializer -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from accounting.models.asset_base import AssetBase @@ -105,4 +105,4 @@ class ModelSerializer( class ViewSerializer(ModelSerializer): """Ticket Base View Model""" - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py index 9eab4637..d6410fae 100644 --- a/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_metadata.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py b/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py index 496b88b8..bc4697c3 100644 --- a/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py +++ b/app/accounting/tests/functional/asset_base/test_functional_asset_base_viewset.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index 8ae20682..02008bad 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -13,7 +13,7 @@ from rest_framework.utils.field_mapping import ClassLookupDict from rest_framework_json_api.utils import get_related_resource_type -from access.models.organization import Organization +from access.models.tenant import Tenant from app.serializers.user import User, UserBaseSerializer @@ -392,9 +392,10 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): "display_name": "Access", "name": "access", "pages": { - 'view_organization': { - "display_name": "Organization", - "name": "organization", + 'view_tenant': { + "display_name": "Tenancy", + "name": "tenant", + "icon": "organization", "link": "/access/organization" }, } @@ -705,7 +706,7 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): # user = view.request.user - user_orgainzations = Organization.objects.filter( + user_orgainzations = Tenant.objects.filter( manager = request.user ) diff --git a/app/api/serializers/access.py b/app/api/serializers/access.py index e53d6ba6..e2986a5f 100644 --- a/app/api/serializers/access.py +++ b/app/api/serializers/access.py @@ -1,7 +1,7 @@ from rest_framework import serializers, request from rest_framework.reverse import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from django.contrib.auth.models import Permission diff --git a/app/api/serializers/auth_token.py b/app/api/serializers/auth_token.py index 84a7b347..3ec3b9c4 100644 --- a/app/api/serializers/auth_token.py +++ b/app/api/serializers/auth_token.py @@ -3,7 +3,7 @@ import re from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.models.tokens import AuthToken from api.serializers import common @@ -132,4 +132,4 @@ class AuthTokenModelSerializer( class AuthTokenViewSerializer(AuthTokenModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/api/serializers/common.py b/app/api/serializers/common.py index c882dd05..0cbb83f6 100644 --- a/app/api/serializers/common.py +++ b/app/api/serializers/common.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.reverse import reverse -from access.serializers.organization import Organization +from access.serializers.organization import Tenant from assistance.models.model_knowledge_base_article import all_models @@ -20,7 +20,7 @@ class OrganizationField(serializers.PrimaryKeyRelatedField): if defined. """ - queryset = Organization.objects.all() + queryset = Tenant.objects.all() if self.context.get('request', None): diff --git a/app/api/tests/functional/auth_token/test_functional_auth_token_serializer.py b/app/api/tests/functional/auth_token/test_functional_auth_token_serializer.py index df9dc02b..1ad1ea62 100644 --- a/app/api/tests/functional/auth_token/test_functional_auth_token_serializer.py +++ b/app/api/tests/functional/auth_token/test_functional_auth_token_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError, PermissionDenied -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from api.serializers.auth_token import AuthToken, AuthTokenModelSerializer diff --git a/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py b/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py index 6bfa4267..297119bb 100644 --- a/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py +++ b/app/api/tests/functional/auth_token/test_functional_auth_token_viewset.py @@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/api/tests/unit/test_index_viewset.py b/app/api/tests/unit/test_index_viewset.py index 08de51ac..c4e0f56c 100644 --- a/app/api/tests/unit/test_index_viewset.py +++ b/app/api/tests/unit/test_index_viewset.py @@ -2,7 +2,7 @@ import django from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from api.tests.unit.test_unit_common_viewset import IndexViewsetInheritedCases diff --git a/app/api/tests/unit/test_navigation_menu.py b/app/api/tests/unit/test_navigation_menu.py index 97816578..c3f67b7d 100644 --- a/app/api/tests/unit/test_navigation_menu.py +++ b/app/api/tests/unit/test_navigation_menu.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers @@ -36,8 +36,8 @@ class NavigationMenu( users_to_create: dict = { 'access': [ { - 'content_model': 'organization', - 'permission_model': 'organization' + 'content_model': 'tenant', + 'permission_model': 'tenant' } ], 'assistance': [ @@ -189,18 +189,18 @@ class NavigationMenu( - def test_navigation_menu_visible_access_organization_exist(self): + def test_navigation_menu_visible_access_tenant_exist(self): """Navigation Menu Check Ensure that if the user has the permission, the navigation menu and page is available for the user """ - nav_menu = self.metadata.get_navigation(self.access_organization) + nav_menu = self.metadata.get_navigation(self.access_tenant) menu_name = 'access' - page_name = 'organization' + page_name = 'tenant' menu_page_exists: bool = False @@ -221,14 +221,14 @@ class NavigationMenu( - def test_navigation_menu_visible_access_organization_no_additional_exist(self): + def test_navigation_menu_visible_access_tenant_no_additional_exist(self): """Navigation Menu Check Ensure that only the navigation menu and entry is the only one displayed for the user who has the desired permission """ - nav_menu = self.metadata.get_navigation(self.access_organization) + nav_menu = self.metadata.get_navigation(self.access_tenant) pages_found: int = 0 diff --git a/app/api/tests/unit/test_unit_common_viewset.py b/app/api/tests/unit/test_unit_common_viewset.py index addb2429..d3f2a4d0 100644 --- a/app/api/tests/unit/test_unit_common_viewset.py +++ b/app/api/tests/unit/test_unit_common_viewset.py @@ -17,7 +17,7 @@ from rest_framework_json_api.metadata import JSONAPIMetadata from access.middleware.request import Tenancy from access.mixins.organization import OrganizationMixin from access.mixins.permissions import OrganizationPermissionMixin -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/api/tests/unit/token/test_token.py b/app/api/tests/unit/token/test_token.py index 0d8d9212..1b6b8a42 100644 --- a/app/api/tests/unit/token/test_token.py +++ b/app/api/tests/unit/token/test_token.py @@ -7,7 +7,7 @@ from datetime import datetime, timedelta from django.shortcuts import reverse from django.test import TestCase, Client -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from api.models.tokens import AuthToken diff --git a/app/api/tests/unit/token/test_unit_auth_token_api_v2.py b/app/api/tests/unit/token/test_unit_auth_token_api_v2.py index a19be6be..762a118f 100644 --- a/app/api/tests/unit/token/test_unit_auth_token_api_v2.py +++ b/app/api/tests/unit/token/test_unit_auth_token_api_v2.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/api/views/access.py b/app/api/views/access.py index 69398194..dd6159bd 100644 --- a/app/api/views/access.py +++ b/app/api/views/access.py @@ -7,7 +7,7 @@ from rest_framework.permissions import DjangoObjectPermissions from rest_framework.response import Response from access.mixin import OrganizationMixin -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from api.serializers.access import OrganizationSerializer, OrganizationListSerializer, TeamSerializer, TeamPermissionSerializer diff --git a/app/api/views/mixin.py b/app/api/views/mixin.py index c1ab247f..1f84c300 100644 --- a/app/api/views/mixin.py +++ b/app/api/views/mixin.py @@ -182,7 +182,7 @@ class OrganizationPermissionAPI(DjangoObjectPermissions, OrganizationMixin): if 'pk' in view.kwargs: - if object_organization is None and view.queryset.model._meta.model_name == 'organization' and view.kwargs['pk']: + if object_organization is None and view.queryset.model._meta.model_name == 'tenant' and view.kwargs['pk']: object_organization = view.kwargs['pk'] diff --git a/app/app/context_processors/base.py b/app/app/context_processors/base.py index 29364926..e03da901 100644 --- a/app/app/context_processors/base.py +++ b/app/app/context_processors/base.py @@ -5,7 +5,7 @@ from app.urls import urlpatterns from django.conf import settings from django.urls import URLPattern, URLResolver -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from settings.models.user_settings import UserSettings diff --git a/app/app/tests/abstract/mock_view.py b/app/app/tests/abstract/mock_view.py index 77f8880c..57444e92 100644 --- a/app/app/tests/abstract/mock_view.py +++ b/app/app/tests/abstract/mock_view.py @@ -2,7 +2,7 @@ import django from access.middleware.request import Tenancy -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from settings.models.app_settings import AppSettings diff --git a/app/app/tests/unit/test_unit_models.py b/app/app/tests/unit/test_unit_models.py index 22567fa7..8233438a 100644 --- a/app/app/tests/unit/test_unit_models.py +++ b/app/app/tests/unit/test_unit_models.py @@ -4,7 +4,7 @@ from django.db.models import fields from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.tests.unit.tenancy_object.test_unit_tenancy_object_model import ( TenancyObjectInheritedCases as AccessTenancyObjectInheritedCases ) diff --git a/app/assistance/migrations/0006_alter_knowledgebase_organization_and_more.py b/app/assistance/migrations/0006_alter_knowledgebase_organization_and_more.py new file mode 100644 index 00000000..7c35348d --- /dev/null +++ b/app/assistance/migrations/0006_alter_knowledgebase_organization_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('assistance', '0005_knowledgebasecategoryhistory_knowledgebasehistory'), + ] + + operations = [ + migrations.AlterField( + model_name='knowledgebase', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='knowledgebasecategory', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='modelknowledgebasearticle', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/assistance/serializers/knowledge_base.py b/app/assistance/serializers/knowledge_base.py index 0e7a41f3..97f2bfd8 100644 --- a/app/assistance/serializers/knowledge_base.py +++ b/app/assistance/serializers/knowledge_base.py @@ -3,7 +3,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers from rest_framework.exceptions import ValidationError -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from access.serializers.teams import TeamBaseSerializer from app.serializers.user import UserBaseSerializer @@ -187,7 +187,7 @@ class KnowledgeBaseViewSerializer(KnowledgeBaseModelSerializer): category = KnowledgeBaseCategoryBaseSerializer( read_only = True ) - organization = OrganizationBaseSerializer( many=False, read_only=True ) + organization = TenantBaseSerializer( many=False, read_only=True ) responsible_teams = TeamBaseSerializer( read_only = True, many = True) diff --git a/app/assistance/serializers/knowledge_base_category.py b/app/assistance/serializers/knowledge_base_category.py index 23860f1e..0e472c4f 100644 --- a/app/assistance/serializers/knowledge_base_category.py +++ b/app/assistance/serializers/knowledge_base_category.py @@ -4,7 +4,7 @@ from rest_framework import serializers from rest_framework.exceptions import ParseError, ValidationError -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from access.serializers.teams import TeamBaseSerializer from app.serializers.user import UserBaseSerializer @@ -182,7 +182,7 @@ class KnowledgeBaseCategoryModelSerializer( class KnowledgeBaseCategoryViewSerializer(KnowledgeBaseCategoryModelSerializer): - organization = OrganizationBaseSerializer( many=False, read_only=True )\ + organization = TenantBaseSerializer( many=False, read_only=True )\ parent_category = KnowledgeBaseCategoryBaseSerializer( many = False, read_only = True) diff --git a/app/assistance/serializers/model_knowledge_base_article.py b/app/assistance/serializers/model_knowledge_base_article.py index ad88c18c..75b6ec35 100644 --- a/app/assistance/serializers/model_knowledge_base_article.py +++ b/app/assistance/serializers/model_knowledge_base_article.py @@ -3,7 +3,7 @@ from django.apps import apps from rest_framework import serializers from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -142,4 +142,4 @@ class ModelKnowledgeBaseArticleViewSerializer(ModelKnowledgeBaseArticleModelSeri category = KnowledgeBaseCategoryBaseSerializer(source = 'article.category', read_only = True ) - organization = OrganizationBaseSerializer( many=False, read_only=True ) + organization = TenantBaseSerializer( many=False, read_only=True ) diff --git a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py index 8d97cbec..b3efb957 100644 --- a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py +++ b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_serializer.py @@ -6,7 +6,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from app.tests.abstract.mock_view import MockView diff --git a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py index 202fab61..4d8dfe43 100644 --- a/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py +++ b/app/assistance/tests/functional/knowledge_base/test_knowledge_base_viewset.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py index c21d6b4c..e926e36d 100644 --- a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py +++ b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_serializer.py @@ -6,7 +6,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from app.tests.abstract.mock_view import MockView diff --git a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py index b1b1869f..0443563e 100644 --- a/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py +++ b/app/assistance/tests/functional/knowledge_base_category/test_knowledge_base_category_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py index d653a9c0..79715bc7 100644 --- a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py +++ b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_serializer.py @@ -6,7 +6,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from assistance.models.model_knowledge_base_article import KnowledgeBase, ModelKnowledgeBaseArticle diff --git a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py index 682c82f2..28395513 100644 --- a/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py +++ b/app/assistance/tests/functional/model_knowledge_base_article/test_model_knowledge_base_article_viewset.py @@ -11,7 +11,7 @@ from django.test import Client, TestCase from rest_framework.reverse import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py index b195c786..f4b8abc3 100644 --- a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py +++ b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py index 310ad732..0c23d0f1 100644 --- a/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py +++ b/app/assistance/tests/unit/knowledge_base/test_knowledge_base_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py index 86de81ee..b22e7f4c 100644 --- a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py +++ b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py index 4b8e6e30..6f21dc0e 100644 --- a/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py +++ b/app/assistance/tests/unit/knowledge_base_category/test_knowledge_base_category_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py b/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py index 6eab5b65..f9c9b9b7 100644 --- a/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py +++ b/app/assistance/tests/unit/model_knowledge_base_article/test_model_knowledge_base_article_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_model.py b/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_model.py index bf1fec70..f2af3b79 100644 --- a/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_model.py +++ b/app/assistance/tests/unit/model_knowledge_base_article/test_unit_model_knowledge_base_article_model.py @@ -3,7 +3,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/config_management/migrations/0008_alter_configgrouphosts_organization_and_more.py b/app/config_management/migrations/0008_alter_configgrouphosts_organization_and_more.py new file mode 100644 index 00000000..9d1f9505 --- /dev/null +++ b/app/config_management/migrations/0008_alter_configgrouphosts_organization_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('config_management', '0007_configgroupshistory_configgrouphostshistory_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='configgrouphosts', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='configgroups', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='configgroupsoftware', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/config_management/serializers/config_group.py b/app/config_management/serializers/config_group.py index 0cafdb52..9f8c8712 100644 --- a/app/config_management/serializers/config_group.py +++ b/app/config_management/serializers/config_group.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.fields import empty from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -200,4 +200,4 @@ class ConfigGroupViewSerializer(ConfigGroupModelSerializer): parent = ConfigGroupBaseSerializer( read_only = True ) - organization = OrganizationBaseSerializer( many=False, read_only=True ) + organization = TenantBaseSerializer( many=False, read_only=True ) diff --git a/app/config_management/serializers/config_group_software.py b/app/config_management/serializers/config_group_software.py index 0a81e74c..9cd991a5 100644 --- a/app/config_management/serializers/config_group_software.py +++ b/app/config_management/serializers/config_group_software.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.fields import empty from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -210,7 +210,7 @@ class ConfigGroupSoftwareViewSerializer(ConfigGroupSoftwareModelSerializer): config_group = ConfigGroupBaseSerializer(read_only = True ) - organization = OrganizationBaseSerializer( many=False, read_only=True ) + organization = TenantBaseSerializer( many=False, read_only=True ) software = SoftwareBaseSerializer( read_only = True ) diff --git a/app/config_management/tests/functional/config_groups/test_config_groups_serializer.py b/app/config_management/tests/functional/config_groups/test_config_groups_serializer.py index 083ce9b3..5c6fa021 100644 --- a/app/config_management/tests/functional/config_groups/test_config_groups_serializer.py +++ b/app/config_management/tests/functional/config_groups/test_config_groups_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py b/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py index 548bf5bc..4cc87b0a 100644 --- a/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py +++ b/app/config_management/tests/functional/config_groups/test_config_groups_viewset.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/config_management/tests/functional/config_groups_software/test_config_groups_software_serializer.py b/app/config_management/tests/functional/config_groups_software/test_config_groups_software_serializer.py index 665f3558..8d356bfe 100644 --- a/app/config_management/tests/functional/config_groups_software/test_config_groups_software_serializer.py +++ b/app/config_management/tests/functional/config_groups_software/test_config_groups_software_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from config_management.serializers.config_group_software import ConfigGroupSoftware, ConfigGroupSoftwareModelSerializer from config_management.models.groups import ConfigGroups diff --git a/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py b/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py index 0294931d..994d47a9 100644 --- a/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py +++ b/app/config_management/tests/functional/config_groups_software/test_config_groups_software_viewset.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/config_management/tests/unit/config_groups/test_config_groups_api.py b/app/config_management/tests/unit/config_groups/test_config_groups_api.py index 4036cdf0..47e1ae06 100644 --- a/app/config_management/tests/unit/config_groups/test_config_groups_api.py +++ b/app/config_management/tests/unit/config_groups/test_config_groups_api.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py b/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py index de629ccd..ff9ca2e9 100644 --- a/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py +++ b/app/config_management/tests/unit/config_groups/test_config_groups_api_v2.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/config_management/tests/unit/config_groups/test_config_groups_permission.py b/app/config_management/tests/unit/config_groups/test_config_groups_permission.py index b46645b7..e3766c06 100644 --- a/app/config_management/tests/unit/config_groups/test_config_groups_permission.py +++ b/app/config_management/tests/unit/config_groups/test_config_groups_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/config_management/tests/unit/config_groups/test_unit_config_groups_model.py b/app/config_management/tests/unit/config_groups/test_unit_config_groups_model.py index 9de6403e..e6f4ade7 100644 --- a/app/config_management/tests/unit/config_groups/test_unit_config_groups_model.py +++ b/app/config_management/tests/unit/config_groups/test_unit_config_groups_model.py @@ -2,7 +2,7 @@ import pytest from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py index 7ccec211..9cc59485 100644 --- a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py +++ b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py index 33dca38b..d25a25a0 100644 --- a/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py +++ b/app/config_management/tests/unit/config_groups_software/test_config_groups_software_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/config_management/tests/unit/config_groups_software/test_unit_config_groups_software_model.py b/app/config_management/tests/unit/config_groups_software/test_unit_config_groups_software_model.py index 8dd5df4d..2509e6ff 100644 --- a/app/config_management/tests/unit/config_groups_software/test_unit_config_groups_software_model.py +++ b/app/config_management/tests/unit/config_groups_software/test_unit_config_groups_software_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/conftest.py b/app/conftest.py index 594a0513..13a5681a 100644 --- a/app/conftest.py +++ b/app/conftest.py @@ -6,7 +6,7 @@ from django.test import ( TestCase ) -from access.models.organization import Organization +from access.models.tenant import Tenant @@ -488,7 +488,7 @@ def organization_one(django_db_blocker): random_str = datetime.datetime.now(tz=datetime.timezone.utc) - item = Organization.objects.create( + item = Tenant.objects.create( name = 'org one from global' + str(random_str) ) @@ -509,7 +509,7 @@ def organization_two(django_db_blocker): random_str = datetime.datetime.now(tz=datetime.timezone.utc) - item = Organization.objects.create( + item = Tenant.objects.create( name = 'org two from global' + str(random_str) ) diff --git a/app/core/forms/common.py b/app/core/forms/common.py index 3e753284..cc2e02f4 100644 --- a/app/core/forms/common.py +++ b/app/core/forms/common.py @@ -1,7 +1,7 @@ from django import forms from django.db.models import Q -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team_user import TeamUsers diff --git a/app/core/lib/slash_commands/linked_model.py b/app/core/lib/slash_commands/linked_model.py index 665be491..ccf01e3f 100644 --- a/app/core/lib/slash_commands/linked_model.py +++ b/app/core/lib/slash_commands/linked_model.py @@ -209,11 +209,11 @@ For this command to process the following conditions must be met: item_type = TicketLinkedItem.Modules.OPERATING_SYSTEM - elif model_type == 'organization': + elif model_type == 'tenant': - from access.models.organization import Organization + from access.models.tenant import Tenant - model = Organization + model = Tenant item_type = TicketLinkedItem.Modules.ORGANIZATION diff --git a/app/core/migrations/0016_data_move_history_to_new_table.py b/app/core/migrations/0016_data_move_history_to_new_table.py index c97b2a5b..d957acae 100644 --- a/app/core/migrations/0016_data_move_history_to_new_table.py +++ b/app/core/migrations/0016_data_move_history_to_new_table.py @@ -6,7 +6,7 @@ from django.conf import settings from django.contrib.auth.models import ContentType from django.db import migrations, models -from access.models.organization_history import Organization, OrganizationHistory +from access.models.organization_history import Tenant, OrganizationHistory from access.models.team_history import Team, TeamHistory from assistance.models.knowledge_base_history import KnowledgeBase, KnowledgeBaseHistory @@ -334,7 +334,7 @@ def model_details(item_pk, item_class) -> dict: case 'organization': - model_class = Organization + model_class = Tenant history_class = OrganizationHistory diff --git a/app/core/migrations/0026_alter_manufacturer_organization_and_more.py b/app/core/migrations/0026_alter_manufacturer_organization_and_more.py new file mode 100644 index 00000000..d808a7e2 --- /dev/null +++ b/app/core/migrations/0026_alter_manufacturer_organization_and_more.py @@ -0,0 +1,76 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('core', '0025_ticketcommentaction'), + ] + + operations = [ + migrations.AlterField( + model_name='manufacturer', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='modelhistory', + name='organization', + field=models.ForeignKey(help_text='Tenant this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='modelnotes', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='notes', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='relatedtickets', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='ticket', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='ticketbase', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='ticketcategory', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='ticketcomment', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='ticketcommentcategory', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='ticketlinkeditem', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/core/mixin/history_save.py b/app/core/mixin/history_save.py index 3e46cb5d..6429cf42 100644 --- a/app/core/mixin/history_save.py +++ b/app/core/mixin/history_save.py @@ -158,7 +158,7 @@ class SaveHistory(models.Model): organization = getattr(self, 'organization', None) - if self._meta.model_name == 'organization': + if self._meta.model_name == 'tenant': organization = self diff --git a/app/core/models/model_history.py b/app/core/models/model_history.py index 4367b204..d5f5cf28 100644 --- a/app/core/models/model_history.py +++ b/app/core/models/model_history.py @@ -7,7 +7,7 @@ from django.db import models from rest_framework.reverse import reverse from access.fields import AutoCreatedField -from access.models.organization import Organization +from access.models.tenant import Tenant from access.models.tenancy import TenancyObject from core.lib.feature_not_used import FeatureNotUsed @@ -72,13 +72,13 @@ class ModelHistory( ) organization = models.ForeignKey( - Organization, + Tenant, blank = False, - help_text = 'Organization this belongs to', + help_text = 'Tenant this belongs to', null = True, on_delete = models.CASCADE, related_name = '+', - verbose_name = 'Organization' + verbose_name = 'Tenant' ) user = models.ForeignKey( diff --git a/app/core/models/ticket/ticket_linked_items.py b/app/core/models/ticket/ticket_linked_items.py index 049acbee..5b11e9fe 100644 --- a/app/core/models/ticket/ticket_linked_items.py +++ b/app/core/models/ticket/ticket_linked_items.py @@ -45,7 +45,7 @@ class TicketLinkedItem(TenancyObject): SOFTWARE = 6, 'Software' KB = 7, 'Knowledge Base' - ORGANIZATION = 8, 'Organization' + TENANT = 8, 'Tenant' TEAM = 9, 'Team' FEATURE_FLAG = 10, 'Feature Flag' SOFTWARE_VERSION = 11, 'Software Version' @@ -152,9 +152,9 @@ class TicketLinkedItem(TenancyObject): item_type = 'operating_system' - elif self.item_type == TicketLinkedItem.Modules.ORGANIZATION: + elif self.item_type == TicketLinkedItem.Modules.TENANT: - item_type = 'organization' + item_type = 'tenant' elif self.item_type == TicketLinkedItem.Modules.SERVICE: diff --git a/app/core/serializers/celery_log.py b/app/core/serializers/celery_log.py index 0da0205a..3b3e3b96 100644 --- a/app/core/serializers/celery_log.py +++ b/app/core/serializers/celery_log.py @@ -5,7 +5,7 @@ from rest_framework import serializers from django_celery_results.models import TaskResult -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from app.serializers.user import UserBaseSerializer diff --git a/app/core/serializers/history.py b/app/core/serializers/history.py index 72db0db1..14337b07 100644 --- a/app/core/serializers/history.py +++ b/app/core/serializers/history.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from app.serializers.user import UserBaseSerializer @@ -138,6 +138,6 @@ class HistoryModelSerializer(HistoryBaseSerializer): class HistoryViewSerializer(HistoryModelSerializer): - organization = OrganizationBaseSerializer( read_only = True ) + organization = TenantBaseSerializer( read_only = True ) user = UserBaseSerializer( read_only = True ) diff --git a/app/core/serializers/manufacturer.py b/app/core/serializers/manufacturer.py index 19bb1046..30a56c2b 100644 --- a/app/core/serializers/manufacturer.py +++ b/app/core/serializers/manufacturer.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from app.serializers.user import UserBaseSerializer @@ -80,4 +80,4 @@ class ManufacturerModelSerializer( class ManufacturerViewSerializer(ManufacturerModelSerializer): - organization = OrganizationBaseSerializer( read_only = True ) + organization = TenantBaseSerializer( read_only = True ) diff --git a/app/core/serializers/model_notes.py b/app/core/serializers/model_notes.py index 2b26e611..6d83a7aa 100644 --- a/app/core/serializers/model_notes.py +++ b/app/core/serializers/model_notes.py @@ -2,7 +2,7 @@ from django.contrib.auth.models import ContentType from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -106,7 +106,7 @@ class ModelNoteModelSerializer( attrs['model_id'] = int(self.fields.fields['model'].context['view'].kwargs['model_id']) - if str(related_model._meta.model_name).lower() == 'organization': + if str(related_model._meta.model_name).lower() == 'tenant': attrs['organization'] = related_model.objects.get( pk = int(self.fields.fields['model'].context['view'].kwargs['model_id']) @@ -142,7 +142,7 @@ class ModelNoteModelSerializer( class ModelNoteViewSerializer(ModelNoteModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) created_by = UserBaseSerializer( many = False, read_only = True ) diff --git a/app/core/serializers/ticket.py b/app/core/serializers/ticket.py index 8b90d05d..b516a2cb 100644 --- a/app/core/serializers/ticket.py +++ b/app/core/serializers/ticket.py @@ -4,7 +4,7 @@ from rest_framework.reverse import reverse from drf_spectacular.utils import extend_schema_serializer from access.serializers.entity import BaseSerializer as EntityBaseSerializer -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -411,7 +411,7 @@ class ViewSerializer(ModelSerializer): opened_by = UserBaseSerializer() - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) parent_ticket = BaseSerializer() diff --git a/app/core/serializers/ticket_category.py b/app/core/serializers/ticket_category.py index a7ed0f91..1533fbc8 100644 --- a/app/core/serializers/ticket_category.py +++ b/app/core/serializers/ticket_category.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -102,4 +102,4 @@ class TicketCategoryModelSerializer( class TicketCategoryViewSerializer(TicketCategoryModelSerializer): - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/core/serializers/ticket_comment.py b/app/core/serializers/ticket_comment.py index 28487a11..9d7e4f78 100644 --- a/app/core/serializers/ticket_comment.py +++ b/app/core/serializers/ticket_comment.py @@ -7,7 +7,7 @@ from rest_framework import serializers from drf_spectacular.utils import extend_schema_serializer from access.serializers.entity import BaseSerializer as EntityBaseSerializer -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common from api.exceptions import UnknownTicketType @@ -319,7 +319,7 @@ class ViewSerializer(ModelSerializer): category = TicketCommentCategoryBaseSerializer( many = False, read_only = True ) - organization = OrganizationBaseSerializer( many = False ) + organization = TenantBaseSerializer( many = False ) parent = BaseSerializer() diff --git a/app/core/serializers/ticket_comment_category.py b/app/core/serializers/ticket_comment_category.py index d5226804..97cfbabb 100644 --- a/app/core/serializers/ticket_comment_category.py +++ b/app/core/serializers/ticket_comment_category.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from app.serializers.user import UserBaseSerializer @@ -117,4 +117,4 @@ class TicketCommentCategoryModelSerializer( class TicketCommentCategoryViewSerializer(TicketCommentCategoryModelSerializer): - organization = OrganizationBaseSerializer( read_only = True ) + organization = TenantBaseSerializer( read_only = True ) diff --git a/app/core/serializers/ticket_comment_depreciated.py b/app/core/serializers/ticket_comment_depreciated.py index ba1a3dab..4fe2d90d 100644 --- a/app/core/serializers/ticket_comment_depreciated.py +++ b/app/core/serializers/ticket_comment_depreciated.py @@ -3,7 +3,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers from rest_framework.fields import empty -from access.serializers.organization import Organization, OrganizationBaseSerializer +from access.serializers.organization import Organization, TenantBaseSerializer from access.serializers.teams import TeamBaseSerializer from api.serializers import common @@ -683,7 +683,7 @@ class TicketCommentImportModelSerializer(TicketCommentModelSerializer): class TicketCommentViewSerializer(TicketCommentModelSerializer): - organization = OrganizationBaseSerializer( many = False ) + organization = TenantBaseSerializer( many = False ) category = TicketCommentCategoryBaseSerializer( many = False, read_only = True ) diff --git a/app/core/serializers/ticket_depreciated.py b/app/core/serializers/ticket_depreciated.py index 1eb89cd7..deebc041 100644 --- a/app/core/serializers/ticket_depreciated.py +++ b/app/core/serializers/ticket_depreciated.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from access.serializers.teams import TeamBaseSerializer from app.serializers.user import UserBaseSerializer @@ -351,7 +351,7 @@ class TicketViewSerializer(TicketModelSerializer): opened_by = UserBaseSerializer() - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) project = ProjectBaseSerializer(many=False, read_only=True) diff --git a/app/core/serializers/ticket_linked_item.py b/app/core/serializers/ticket_linked_item.py index 6dd35569..db512e90 100644 --- a/app/core/serializers/ticket_linked_item.py +++ b/app/core/serializers/ticket_linked_item.py @@ -2,7 +2,7 @@ from rest_framework.fields import empty from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from assistance.serializers.request import TicketBaseSerializer @@ -157,7 +157,7 @@ class TicketLinkedItemModelSerializer( class TicketLinkedItemViewSerializer(TicketLinkedItemModelSerializer): - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) item = serializers.SerializerMethodField('get_item') @@ -214,11 +214,11 @@ class TicketLinkedItemViewSerializer(TicketLinkedItemModelSerializer): model = OperatingSystem - elif item.item_type == TicketLinkedItem.Modules.ORGANIZATION: + elif item.item_type == TicketLinkedItem.Modules.TENANT: - from access.serializers.organization import Organization, OrganizationBaseSerializer + from access.serializers.organization import Organization, TenantBaseSerializer - base_serializer = OrganizationBaseSerializer + base_serializer = TenantBaseSerializer model = Organization diff --git a/app/core/serializers/ticket_related.py b/app/core/serializers/ticket_related.py index 197aca45..ff61cb91 100644 --- a/app/core/serializers/ticket_related.py +++ b/app/core/serializers/ticket_related.py @@ -2,7 +2,7 @@ from rest_framework.fields import empty from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -151,6 +151,6 @@ class RelatedTicketViewSerializer(RelatedTicketModelSerializer): from_ticket_id = TicketBaseSerializer() - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) to_ticket_id = TicketBaseSerializer() diff --git a/app/core/tests/abstract/model_notes_api_fields.py b/app/core/tests/abstract/model_notes_api_fields.py index da9adbc5..a8a959f2 100644 --- a/app/core/tests/abstract/model_notes_api_fields.py +++ b/app/core/tests/abstract/model_notes_api_fields.py @@ -7,7 +7,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/abstract/test_functional_history.py b/app/core/tests/abstract/test_functional_history.py index 62b5286a..23832cc3 100644 --- a/app/core/tests/abstract/test_functional_history.py +++ b/app/core/tests/abstract/test_functional_history.py @@ -1,6 +1,6 @@ from django.contrib.auth.models import ContentType -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization diff --git a/app/core/tests/abstract/test_functional_notes_viewset.py b/app/core/tests/abstract/test_functional_notes_viewset.py index 57570151..d3adaf88 100644 --- a/app/core/tests/abstract/test_functional_notes_viewset.py +++ b/app/core/tests/abstract/test_functional_notes_viewset.py @@ -3,7 +3,7 @@ import django from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/abstract/test_item_ticket_api_v2.py b/app/core/tests/abstract/test_item_ticket_api_v2.py index 03214f5e..0f10a39e 100644 --- a/app/core/tests/abstract/test_item_ticket_api_v2.py +++ b/app/core/tests/abstract/test_item_ticket_api_v2.py @@ -7,7 +7,7 @@ from django.test import TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/abstract/test_ticket_api_v2.py b/app/core/tests/abstract/test_ticket_api_v2.py index 6fe6a4f3..fa6fba6e 100644 --- a/app/core/tests/abstract/test_ticket_api_v2.py +++ b/app/core/tests/abstract/test_ticket_api_v2.py @@ -7,7 +7,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/abstract/test_ticket_serializer.py b/app/core/tests/abstract/test_ticket_serializer.py index 0ef800db..9c0b5885 100644 --- a/app/core/tests/abstract/test_ticket_serializer.py +++ b/app/core/tests/abstract/test_ticket_serializer.py @@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django.test import TestCase from access.middleware.request import Tenancy -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/abstract/test_ticket_viewset.py b/app/core/tests/abstract/test_ticket_viewset.py index bd6610e2..be477923 100644 --- a/app/core/tests/abstract/test_ticket_viewset.py +++ b/app/core/tests/abstract/test_ticket_viewset.py @@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/abstract/test_unit_model_history_api_v2.py b/app/core/tests/abstract/test_unit_model_history_api_v2.py index bb2d3977..cb148a54 100644 --- a/app/core/tests/abstract/test_unit_model_history_api_v2.py +++ b/app/core/tests/abstract/test_unit_model_history_api_v2.py @@ -5,7 +5,7 @@ from django.test import Client from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/abstract/test_unit_model_notes_serializer.py b/app/core/tests/abstract/test_unit_model_notes_serializer.py index 0f8175d8..aecb7f7e 100644 --- a/app/core/tests/abstract/test_unit_model_notes_serializer.py +++ b/app/core/tests/abstract/test_unit_model_notes_serializer.py @@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView diff --git a/app/core/tests/functional/manufacturer/test_manufacturer_serializer.py b/app/core/tests/functional/manufacturer/test_manufacturer_serializer.py index fabc0815..fd659e46 100644 --- a/app/core/tests/functional/manufacturer/test_manufacturer_serializer.py +++ b/app/core/tests/functional/manufacturer/test_manufacturer_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.serializers.manufacturer import Manufacturer, ManufacturerModelSerializer diff --git a/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py b/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py index 3f36928e..cb4a51ce 100644 --- a/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py +++ b/app/core/tests/functional/manufacturer/test_manufacturer_viewset.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py b/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py index a56dab90..2252d05f 100644 --- a/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py +++ b/app/core/tests/functional/related_ticket/test_related_ticket_serializer.py @@ -5,7 +5,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView diff --git a/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py b/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py index 6e59bf8c..43f7fb51 100644 --- a/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py +++ b/app/core/tests/functional/related_ticket/test_related_ticket_viewset.py @@ -10,7 +10,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/slash_commands/test_slash_command_related.py b/app/core/tests/functional/slash_commands/test_slash_command_related.py index 176a2059..417010c0 100644 --- a/app/core/tests/functional/slash_commands/test_slash_command_related.py +++ b/app/core/tests/functional/slash_commands/test_slash_command_related.py @@ -5,7 +5,7 @@ import unittest from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.person import Person from core.models.ticket.ticket import Ticket diff --git a/app/core/tests/functional/test_history/test_functional_history_viewset.py b/app/core/tests/functional/test_history/test_functional_history_viewset.py index d25ececf..88084332 100644 --- a/app/core/tests/functional/test_history/test_functional_history_viewset.py +++ b/app/core/tests/functional/test_history/test_functional_history_viewset.py @@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/test_task_result/test_task_result_viewset.py b/app/core/tests/functional/test_task_result/test_task_result_viewset.py index 4c99c8b1..38ba344e 100644 --- a/app/core/tests/functional/test_task_result/test_task_result_viewset.py +++ b/app/core/tests/functional/test_task_result/test_task_result_viewset.py @@ -9,7 +9,7 @@ from django import urls from django_celery_results.models import TaskResult -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py index de66dcc7..002ba846 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_metadata.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py b/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py index 5ce752a8..320be4f7 100644 --- a/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py +++ b/app/core/tests/functional/ticket_base/test_functional_ticket_base_viewset.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/ticket_category/test_ticket_category_serializer.py b/app/core/tests/functional/ticket_category/test_ticket_category_serializer.py index e574e831..a1ff55ef 100644 --- a/app/core/tests/functional/ticket_category/test_ticket_category_serializer.py +++ b/app/core/tests/functional/ticket_category/test_ticket_category_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py b/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py index 148f1923..c7ff2ea1 100644 --- a/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py +++ b/app/core/tests/functional/ticket_category/test_ticket_category_viewset.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py b/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py index b66f75c8..59c96f6e 100644 --- a/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py +++ b/app/core/tests/functional/ticket_comment/test_ticket_comment_serializer.py @@ -6,7 +6,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError from access.middleware.request import Tenancy -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.serializers.ticket_comment_depreciated import ( Ticket, diff --git a/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py b/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py index facad4f3..999adf7b 100644 --- a/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py +++ b/app/core/tests/functional/ticket_comment/test_ticket_comment_viewset.py @@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_serializer.py b/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_serializer.py index f6336a99..fa239694 100644 --- a/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_serializer.py +++ b/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py b/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py index 4c3c4f7c..b867f37e 100644 --- a/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py +++ b/app/core/tests/functional/ticket_comment_category/test_ticket_comment_category_viewset.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py index 5c0f77af..0fca1072 100644 --- a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py +++ b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_serializer.py @@ -5,7 +5,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.serializers.ticket_linked_item import Ticket, TicketLinkedItem, TicketLinkedItemModelSerializer diff --git a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py index 4478cb9c..230692d6 100644 --- a/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py +++ b/app/core/tests/functional/ticket_linked_item/test_ticket_linked_item_viewset.py @@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py b/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py index afdf1f99..8d5c48ab 100644 --- a/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py +++ b/app/core/tests/unit/manufacturer/test_manufacturer_api_v2.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/manufacturer/test_manufacturer_permission.py b/app/core/tests/unit/manufacturer/test_manufacturer_permission.py index cbe2784a..7b462336 100644 --- a/app/core/tests/unit/manufacturer/test_manufacturer_permission.py +++ b/app/core/tests/unit/manufacturer/test_manufacturer_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/model_notes/test_unit_model_notes_model.py b/app/core/tests/unit/model_notes/test_unit_model_notes_model.py index 8543aa16..aedc2df5 100644 --- a/app/core/tests/unit/model_notes/test_unit_model_notes_model.py +++ b/app/core/tests/unit/model_notes/test_unit_model_notes_model.py @@ -3,7 +3,7 @@ import django from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import TenancyObjectInheritedCases diff --git a/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py b/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py index 09aa54fc..e75b1fa8 100644 --- a/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py +++ b/app/core/tests/unit/related_ticket/test_related_ticket_api_v2.py @@ -7,7 +7,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/test_history/test_unit_history_model.py b/app/core/tests/unit/test_history/test_unit_history_model.py index 2c5b2aca..15c6bcfb 100644 --- a/app/core/tests/unit/test_history/test_unit_history_model.py +++ b/app/core/tests/unit/test_history/test_unit_history_model.py @@ -3,7 +3,7 @@ import django from django.contrib.auth.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import TenancyObjectInheritedCases diff --git a/app/core/tests/unit/test_task_result/test_task_result.py b/app/core/tests/unit/test_task_result/test_task_result.py index 07806dc6..3518114c 100644 --- a/app/core/tests/unit/test_task_result/test_task_result.py +++ b/app/core/tests/unit/test_task_result/test_task_result.py @@ -10,7 +10,7 @@ from django.test import TestCase from celery import states -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/test_task_result/test_task_result_api_v2.py b/app/core/tests/unit/test_task_result/test_task_result_api_v2.py index 716817b3..5b552092 100644 --- a/app/core/tests/unit/test_task_result/test_task_result_api_v2.py +++ b/app/core/tests/unit/test_task_result/test_task_result_api_v2.py @@ -11,7 +11,7 @@ from django_celery_results.models import TaskResult from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py b/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py index 48c17215..5a3de87b 100644 --- a/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py +++ b/app/core/tests/unit/ticket_category/test_ticket_category_api_v2.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_category/test_ticket_category_permission.py b/app/core/tests/unit/ticket_category/test_ticket_category_permission.py index d1ffe0ee..8d18a7b8 100644 --- a/app/core/tests/unit/ticket_category/test_ticket_category_permission.py +++ b/app/core/tests/unit/ticket_category/test_ticket_category_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py b/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py index f08f6f97..60397776 100644 --- a/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py +++ b/app/core/tests/unit/ticket_category/test_ticket_category_permission_api.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py b/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py index 1e1840ef..49a102ac 100644 --- a/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py +++ b/app/core/tests/unit/ticket_comment/test_ticket_comment_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py index 8febfe90..a9bd599f 100644 --- a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py +++ b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission.py @@ -11,7 +11,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py index f4629561..84ba460f 100644 --- a/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py +++ b/app/core/tests/unit/ticket_comment/test_ticket_comment_permission_api.py @@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py b/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py index 988c2dc2..960591e2 100644 --- a/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py +++ b/app/core/tests/unit/ticket_comment/test_unit_ticket_comment_model.py @@ -3,7 +3,7 @@ import pytest from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py index 7bab6eac..ff421c1a 100644 --- a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py +++ b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_api_v2.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py index 7df72b65..d2a1dee0 100644 --- a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py +++ b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py index 4093e9ce..a51d6095 100644 --- a/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py +++ b/app/core/tests/unit/ticket_comment_category/test_ticket_comment_category_permission_api.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py index 326a5958..57b81060 100644 --- a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py +++ b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission.py @@ -13,7 +13,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py index d19585a1..dc661c24 100644 --- a/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py +++ b/app/core/tests/unit/ticket_depreciated/test_ticket_depreciated_permission_api.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py index ccd5e6f0..91be407c 100644 --- a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py +++ b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_api_v2.py @@ -7,7 +7,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py index df547a34..b899da73 100644 --- a/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py +++ b/app/core/tests/unit/ticket_linked_item/test_ticket_linked_item_permission.py @@ -11,7 +11,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py b/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py index 8d6f56dd..973c3f53 100644 --- a/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py +++ b/app/core/tests/unit/ticket_linked_item/test_unit_ticket_linked_item_model.py @@ -2,7 +2,7 @@ import django from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from assistance.models.knowledge_base import KnowledgeBase diff --git a/app/core/viewsets/ticket_depreciated.py b/app/core/viewsets/ticket_depreciated.py index 185d4876..fc36d1a0 100644 --- a/app/core/viewsets/ticket_depreciated.py +++ b/app/core/viewsets/ticket_depreciated.py @@ -1,7 +1,7 @@ from api.exceptions import UnknownTicketType from api.viewsets.common import ModelViewSet -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from assistance.serializers.request import ( RequestAddTicketModelSerializer, diff --git a/app/devops/migrations/0012_alter_checkin_organization_and_more.py b/app/devops/migrations/0012_alter_checkin_organization_and_more.py new file mode 100644 index 00000000..cd0b2845 --- /dev/null +++ b/app/devops/migrations/0012_alter_checkin_organization_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('devops', '0011_alter_gitgroup_unique_together_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='checkin', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='featureflag', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='gitgroup', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='gitrepository', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='softwareenablefeatureflag', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/devops/serializers/feature_flag.py b/app/devops/serializers/feature_flag.py index 13aae8ee..e336643a 100644 --- a/app/devops/serializers/feature_flag.py +++ b/app/devops/serializers/feature_flag.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from access.serializers.organization import Organization, OrganizationBaseSerializer +from access.serializers.organization import Organization, TenantBaseSerializer from api.serializers import common @@ -164,6 +164,6 @@ class ModelSerializer( class ViewSerializer(ModelSerializer): - organization = OrganizationBaseSerializer( read_only = True ) + organization = TenantBaseSerializer( read_only = True ) software = SoftwareBaseSerializer( read_only = True ) diff --git a/app/devops/serializers/git_group.py b/app/devops/serializers/git_group.py index 39d74af7..47423ca9 100644 --- a/app/devops/serializers/git_group.py +++ b/app/devops/serializers/git_group.py @@ -2,7 +2,7 @@ from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -125,6 +125,6 @@ class ModelSerializer( @extend_schema_serializer(component_name = 'GitGroupViewSerializer') class ViewSerializer(ModelSerializer): - organization = OrganizationBaseSerializer( read_only = True ) + organization = TenantBaseSerializer( read_only = True ) parent_group = BaseSerializer( read_only = True ) diff --git a/app/devops/serializers/git_repository/base.py b/app/devops/serializers/git_repository/base.py index aa967c38..cd626429 100644 --- a/app/devops/serializers/git_repository/base.py +++ b/app/devops/serializers/git_repository/base.py @@ -2,7 +2,7 @@ from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -112,6 +112,6 @@ class ModelSerializer( @extend_schema_serializer(component_name = 'GitViewSerializer') class ViewSerializer(ModelSerializer): - organization = OrganizationBaseSerializer( read_only = True ) + organization = TenantBaseSerializer( read_only = True ) git_group = GitGroupBaseSerializer( read_only = True ) diff --git a/app/devops/serializers/git_repository/gitlab.py b/app/devops/serializers/git_repository/gitlab.py index f292e9d4..0becf804 100644 --- a/app/devops/serializers/git_repository/gitlab.py +++ b/app/devops/serializers/git_repository/gitlab.py @@ -2,7 +2,7 @@ from drf_spectacular.utils import extend_schema_serializer from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common diff --git a/app/devops/serializers/software_enable_feature_flag.py b/app/devops/serializers/software_enable_feature_flag.py index 66f84a4a..106c6286 100644 --- a/app/devops/serializers/software_enable_feature_flag.py +++ b/app/devops/serializers/software_enable_feature_flag.py @@ -1,7 +1,7 @@ # from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -98,6 +98,6 @@ class ModelSerializer( class ViewSerializer(ModelSerializer): - organization = OrganizationBaseSerializer( read_only = True ) + organization = TenantBaseSerializer( read_only = True ) software = SoftwareBaseSerializer( read_only = True ) diff --git a/app/devops/tests/functional/check_ins/test_functional_check_ins_create.py b/app/devops/tests/functional/check_ins/test_functional_check_ins_create.py index 15471843..ea808e57 100644 --- a/app/devops/tests/functional/check_ins/test_functional_check_ins_create.py +++ b/app/devops/tests/functional/check_ins/test_functional_check_ins_create.py @@ -2,7 +2,7 @@ from django.test import Client, TestCase from rest_framework.reverse import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from devops.models.feature_flag_history import FeatureFlag from devops.models.software_enable_feature_flag import SoftwareEnableFeatureFlag diff --git a/app/devops/tests/functional/feature_flag/test_functional_feature_flag_serializer.py b/app/devops/tests/functional/feature_flag/test_functional_feature_flag_serializer.py index 95ab2b0b..4bee8d43 100644 --- a/app/devops/tests/functional/feature_flag/test_functional_feature_flag_serializer.py +++ b/app/devops/tests/functional/feature_flag/test_functional_feature_flag_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from devops.models.software_enable_feature_flag import SoftwareEnableFeatureFlag from devops.serializers.feature_flag import FeatureFlag, ModelSerializer diff --git a/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py b/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py index a0c46782..3381d112 100644 --- a/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py +++ b/app/devops/tests/functional/feature_flag/test_functional_feature_flag_viewset.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import ( from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_serializer.py b/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_serializer.py index fcd4a6f9..ba9964e9 100644 --- a/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_serializer.py +++ b/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from devops.models.software_enable_feature_flag import SoftwareEnableFeatureFlag from devops.serializers.public_feature_flag import FeatureFlag, ViewSerializer diff --git a/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_viewset.py b/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_viewset.py index da54e3af..36a38e70 100644 --- a/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_viewset.py +++ b/app/devops/tests/functional/public_feature_flag/test_functional_public_feature_flag_viewset.py @@ -4,7 +4,7 @@ from dateutil import tz from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from api.tests.abstract.api_serializer_viewset import SerializerView diff --git a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py index a4f92687..bd11e74e 100644 --- a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py +++ b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_serializer.py @@ -5,7 +5,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView diff --git a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py index cd8154c5..e2eb72a0 100644 --- a/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py +++ b/app/devops/tests/functional/software_enable_feature_flag/test_functional_software_enable_feature_flag_viewset.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from django.shortcuts import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/devops/tests/unit/check_ins/test_unit_check_ins_model.py b/app/devops/tests/unit/check_ins/test_unit_check_ins_model.py index 1d0ea807..75e54abf 100644 --- a/app/devops/tests/unit/check_ins/test_unit_check_ins_model.py +++ b/app/devops/tests/unit/check_ins/test_unit_check_ins_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py b/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py index 19ba8071..2aec7120 100644 --- a/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py +++ b/app/devops/tests/unit/feature_flag/test_unit_feature_flag_api_v2.py @@ -7,7 +7,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/devops/tests/unit/feature_flag/test_unit_feature_flag_model.py b/app/devops/tests/unit/feature_flag/test_unit_feature_flag_model.py index 2549a865..4cd2fdad 100644 --- a/app/devops/tests/unit/feature_flag/test_unit_feature_flag_model.py +++ b/app/devops/tests/unit/feature_flag/test_unit_feature_flag_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/devops/tests/unit/feature_flag_notes/test_unit_feature_flag_notes_model.py b/app/devops/tests/unit/feature_flag_notes/test_unit_feature_flag_notes_model.py index 4c741ff6..f4743fdc 100644 --- a/app/devops/tests/unit/feature_flag_notes/test_unit_feature_flag_notes_model.py +++ b/app/devops/tests/unit/feature_flag_notes/test_unit_feature_flag_notes_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.tests.unit.model_notes.test_unit_model_notes_model import ( ModelNotesInheritedCases diff --git a/app/devops/tests/unit/public_feature_flag/test_unit_public_feature_flag_api_v2.py b/app/devops/tests/unit/public_feature_flag/test_unit_public_feature_flag_api_v2.py index e49cb7d3..2ac4c0b6 100644 --- a/app/devops/tests/unit/public_feature_flag/test_unit_public_feature_flag_api_v2.py +++ b/app/devops/tests/unit/public_feature_flag/test_unit_public_feature_flag_api_v2.py @@ -5,7 +5,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from devops.models.feature_flag import FeatureFlag from devops.models.software_enable_feature_flag import SoftwareEnableFeatureFlag diff --git a/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py b/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py index 60b50371..10f9f6aa 100644 --- a/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py +++ b/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_api_v2.py @@ -7,7 +7,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_model.py b/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_model.py index f6404bcf..a6398506 100644 --- a/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_model.py +++ b/app/devops/tests/unit/software_enable_feature_flag/test_unit_software_enable_feature_flag_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/human_resources/serializers/entity_employee.py b/app/human_resources/serializers/entity_employee.py index bbe28e9c..115a3614 100644 --- a/app/human_resources/serializers/entity_employee.py +++ b/app/human_resources/serializers/entity_employee.py @@ -4,7 +4,7 @@ from access.serializers.entity_contact import ( BaseSerializer as ContactBaseSerializer, ModelSerializer as ContactModelSerializer, ) -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from human_resources.models.employee import Employee @@ -73,4 +73,4 @@ class ViewSerializer( This model inherits from the Contact model. """ - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/itam/forms/software/update.py b/app/itam/forms/software/update.py index e9fd346f..7aa67703 100644 --- a/app/itam/forms/software/update.py +++ b/app/itam/forms/software/update.py @@ -38,7 +38,7 @@ class SoftwareChange(SoftwareForm): ) self.fields['organization'] = forms.CharField( - label = 'Organization', + label = 'Tenant', initial = self.instance.organization ) @@ -102,7 +102,7 @@ class DetailForm(SoftwareForm): self.fields[ 'organization' ] = forms.CharField( - label = 'Organization', + label = 'Tenant', initial = self.instance.organization ) diff --git a/app/itam/migrations/0012_alter_device_organization_and_more.py b/app/itam/migrations/0012_alter_device_organization_and_more.py new file mode 100644 index 00000000..24050cfd --- /dev/null +++ b/app/itam/migrations/0012_alter_device_organization_and_more.py @@ -0,0 +1,66 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('itam', '0011_itamassetbase'), + ] + + operations = [ + migrations.AlterField( + model_name='device', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='devicemodel', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='deviceoperatingsystem', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='devicesoftware', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='devicetype', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='operatingsystem', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='operatingsystemversion', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='software', + name='organization', + field=models.ForeignKey(help_text='Tenant this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='software', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='softwarecategory', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='softwareversion', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/itam/models/software.py b/app/itam/models/software.py index be0b65d5..2ce2af02 100644 --- a/app/itam/models/software.py +++ b/app/itam/models/software.py @@ -5,7 +5,7 @@ from django.dispatch import receiver from rest_framework.reverse import reverse from access.fields import * -from access.models.tenancy import Organization, TenancyObject +from access.models.tenancy import Tenant, TenancyObject from core.mixin.history_save import SaveHistory from core.models.manufacturer import Manufacturer @@ -148,14 +148,14 @@ class Software(SoftwareCommonFields, SaveHistory): organization = models.ForeignKey( - Organization, + Tenant, blank = False, - help_text = 'Organization this belongs to', + help_text = 'Tenant this belongs to', null = False, on_delete = models.CASCADE, related_name = 'software', validators = [ TenancyObject.validatate_organization_exists ], - verbose_name = 'Organization', + verbose_name = 'Tenant', ) publisher = models.ForeignKey( diff --git a/app/itam/serializers/asset_it_asset.py b/app/itam/serializers/asset_it_asset.py index db9e3191..650e531d 100644 --- a/app/itam/serializers/asset_it_asset.py +++ b/app/itam/serializers/asset_it_asset.py @@ -2,7 +2,7 @@ from rest_framework import serializers from drf_spectacular.utils import extend_schema_serializer -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from accounting.serializers.asset import ( BaseSerializer, @@ -78,4 +78,4 @@ class ViewSerializer( ): """IT Asset Base View Model""" - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/itam/serializers/device.py b/app/itam/serializers/device.py index e50e4e5a..28217d21 100644 --- a/app/itam/serializers/device.py +++ b/app/itam/serializers/device.py @@ -5,7 +5,7 @@ from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResp from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common from api.viewsets.common import ModelViewSet @@ -153,4 +153,4 @@ class DeviceViewSerializer(DeviceModelSerializer): device_type = DeviceTypeBaseSerializer( many = False, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/itam/serializers/device_model.py b/app/itam/serializers/device_model.py index a3873181..84276aef 100644 --- a/app/itam/serializers/device_model.py +++ b/app/itam/serializers/device_model.py @@ -2,7 +2,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -84,5 +84,5 @@ class DeviceModelViewSerializer(DeviceModelModelSerializer): manufacturer = ManufacturerBaseSerializer( many = False, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/itam/serializers/device_notes.py b/app/itam/serializers/device_notes.py index be3a696c..c22ccc87 100644 --- a/app/itam/serializers/device_notes.py +++ b/app/itam/serializers/device_notes.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common diff --git a/app/itam/serializers/device_operating_system.py b/app/itam/serializers/device_operating_system.py index dd9778e8..64f3f266 100644 --- a/app/itam/serializers/device_operating_system.py +++ b/app/itam/serializers/device_operating_system.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.fields import empty from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -173,6 +173,6 @@ class DeviceOperatingSystemViewSerializer(DeviceOperatingSystemModelSerializer): operating_system_version = OperatingSystemVersionBaseSerializer(many=False, read_only=True) - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/itam/serializers/device_software.py b/app/itam/serializers/device_software.py index 449caf90..3c51ac8f 100644 --- a/app/itam/serializers/device_software.py +++ b/app/itam/serializers/device_software.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.fields import empty from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -206,7 +206,7 @@ class DeviceSoftwareViewSerializer(DeviceSoftwareModelSerializer): installedversion = SoftwareVersionBaseSerializer(many=False, read_only=True) - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) software = SoftwareBaseSerializer(many=False, read_only=True) diff --git a/app/itam/serializers/device_type.py b/app/itam/serializers/device_type.py index ae3f0175..c49b04f9 100644 --- a/app/itam/serializers/device_type.py +++ b/app/itam/serializers/device_type.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -78,5 +78,5 @@ class DeviceTypeModelSerializer( class DeviceTypeViewSerializer(DeviceTypeModelSerializer): - organization = OrganizationBaseSerializer(many=False, read_only=True) + organization = TenantBaseSerializer(many=False, read_only=True) diff --git a/app/itam/serializers/operating_system.py b/app/itam/serializers/operating_system.py index 5fb34467..067f95a3 100644 --- a/app/itam/serializers/operating_system.py +++ b/app/itam/serializers/operating_system.py @@ -2,7 +2,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -103,7 +103,7 @@ class OperatingSystemModelSerializer( class OperatingSystemViewSerializer(OperatingSystemModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) publisher = ManufacturerBaseSerializer( many = False, read_only = True ) diff --git a/app/itam/serializers/operating_system_version.py b/app/itam/serializers/operating_system_version.py index b23f5c76..76a929ca 100644 --- a/app/itam/serializers/operating_system_version.py +++ b/app/itam/serializers/operating_system_version.py @@ -2,7 +2,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -109,5 +109,5 @@ class OperatingSystemVersionViewSerializer(OperatingSystemVersionModelSerializer operating_system = OperatingSystemBaseSerializer( many = False, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/itam/serializers/software.py b/app/itam/serializers/software.py index 1dab52b4..4ee6b6f5 100644 --- a/app/itam/serializers/software.py +++ b/app/itam/serializers/software.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -125,6 +125,6 @@ class SoftwareViewSerializer(SoftwareModelSerializer): category = SoftwareCategoryBaseSerializer( many = False, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) publisher = ManufacturerBaseSerializer( many = False, read_only = True ) diff --git a/app/itam/serializers/software_category.py b/app/itam/serializers/software_category.py index 7947679a..c0aba9dd 100644 --- a/app/itam/serializers/software_category.py +++ b/app/itam/serializers/software_category.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -86,4 +86,4 @@ class SoftwareCategoryModelSerializer( class SoftwareCategoryViewSerializer(SoftwareCategoryModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/itam/serializers/software_version.py b/app/itam/serializers/software_version.py index 3209050e..b6922b84 100644 --- a/app/itam/serializers/software_version.py +++ b/app/itam/serializers/software_version.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -120,4 +120,4 @@ class SoftwareVersionViewSerializer(SoftwareVersionModelSerializer): software = SoftwareBaseSerializer( many = False, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/itam/tasks/inventory.py b/app/itam/tasks/inventory.py index d9915844..5eed3b5f 100644 --- a/app/itam/tasks/inventory.py +++ b/app/itam/tasks/inventory.py @@ -7,7 +7,7 @@ from celery import shared_task, current_task from celery.utils.log import get_task_logger from celery import states -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from itam.serializers.inventory import InventorySerializer diff --git a/app/itam/tests/functional/device/test_device_serializer.py b/app/itam/tests/functional/device/test_device_serializer.py index cf3ddf01..f18e96a5 100644 --- a/app/itam/tests/functional/device/test_device_serializer.py +++ b/app/itam/tests/functional/device/test_device_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/device/test_device_viewset.py b/app/itam/tests/functional/device/test_device_viewset.py index 4722a555..2fff981c 100644 --- a/app/itam/tests/functional/device/test_device_viewset.py +++ b/app/itam/tests/functional/device/test_device_viewset.py @@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/device_model/test_device_model_serializer.py b/app/itam/tests/functional/device_model/test_device_model_serializer.py index ed93649f..6d4e11ba 100644 --- a/app/itam/tests/functional/device_model/test_device_model_serializer.py +++ b/app/itam/tests/functional/device_model/test_device_model_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/device_model/test_device_model_viewset.py b/app/itam/tests/functional/device_model/test_device_model_viewset.py index d5eb308f..da816093 100644 --- a/app/itam/tests/functional/device_model/test_device_model_viewset.py +++ b/app/itam/tests/functional/device_model/test_device_model_viewset.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py b/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py index 2a9ec334..d988d2e0 100644 --- a/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py +++ b/app/itam/tests/functional/device_operating_system/test_device_operating_system_serializer.py @@ -7,7 +7,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError from access.middleware.request import Tenancy -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from itam.serializers.device_operating_system import Device, DeviceOperatingSystem, DeviceOperatingSystemModelSerializer from itam.models.operating_system import OperatingSystem, OperatingSystemVersion diff --git a/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py b/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py index e592580c..ae6d86e0 100644 --- a/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py +++ b/app/itam/tests/functional/device_operating_system/test_device_operating_system_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/device_software/test_device_software_serializer.py b/app/itam/tests/functional/device_software/test_device_software_serializer.py index bb379f37..fd418538 100644 --- a/app/itam/tests/functional/device_software/test_device_software_serializer.py +++ b/app/itam/tests/functional/device_software/test_device_software_serializer.py @@ -6,7 +6,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError from access.middleware.request import Tenancy -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from itam.serializers.device_software import Device, DeviceSoftware, DeviceSoftwareModelSerializer from itam.models.software import Software, SoftwareCategory, SoftwareVersion diff --git a/app/itam/tests/functional/device_software/test_device_software_viewset.py b/app/itam/tests/functional/device_software/test_device_software_viewset.py index f63c2cf4..6691d958 100644 --- a/app/itam/tests/functional/device_software/test_device_software_viewset.py +++ b/app/itam/tests/functional/device_software/test_device_software_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/device_type/test_device_type_serializer.py b/app/itam/tests/functional/device_type/test_device_type_serializer.py index 1db5ae90..85a709bb 100644 --- a/app/itam/tests/functional/device_type/test_device_type_serializer.py +++ b/app/itam/tests/functional/device_type/test_device_type_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/device_type/test_device_type_viewset.py b/app/itam/tests/functional/device_type/test_device_type_viewset.py index 724e74e9..7a50039f 100644 --- a/app/itam/tests/functional/device_type/test_device_type_viewset.py +++ b/app/itam/tests/functional/device_type/test_device_type_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py b/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py index bf0e4c62..ad7d1005 100644 --- a/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py +++ b/app/itam/tests/functional/endpoint_operating_system_installs/test_operating_system_installs_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py index 49236e9a..5e2704e4 100644 --- a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py +++ b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_serializer.py @@ -6,7 +6,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError from access.middleware.request import Tenancy -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from itam.serializers.device_software import Device, DeviceSoftware, DeviceSoftwareModelSerializer, SoftwareInstallsModelSerializer from itam.models.software import Software, SoftwareCategory, SoftwareVersion diff --git a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py index d563e81d..ffa52355 100644 --- a/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py +++ b/app/itam/tests/functional/endpoint_software_installs/test_software_installs_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/inventory/test_inventory_serializer.py b/app/itam/tests/functional/inventory/test_inventory_serializer.py index c078a71e..6160f9aa 100644 --- a/app/itam/tests/functional/inventory/test_inventory_serializer.py +++ b/app/itam/tests/functional/inventory/test_inventory_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from itam.serializers.inventory import ( InventorySerializer diff --git a/app/itam/tests/functional/inventory/test_inventory_viewset.py b/app/itam/tests/functional/inventory/test_inventory_viewset.py index a904f99c..1acecb42 100644 --- a/app/itam/tests/functional/inventory/test_inventory_viewset.py +++ b/app/itam/tests/functional/inventory/test_inventory_viewset.py @@ -9,7 +9,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/operating_system/test_operating_system_serializer.py b/app/itam/tests/functional/operating_system/test_operating_system_serializer.py index 064e44bf..ddeae281 100644 --- a/app/itam/tests/functional/operating_system/test_operating_system_serializer.py +++ b/app/itam/tests/functional/operating_system/test_operating_system_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/operating_system/test_operating_system_viewset.py b/app/itam/tests/functional/operating_system/test_operating_system_viewset.py index e0f638ae..674bd43d 100644 --- a/app/itam/tests/functional/operating_system/test_operating_system_viewset.py +++ b/app/itam/tests/functional/operating_system/test_operating_system_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py b/app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py index a8e33732..8989a72b 100644 --- a/app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py +++ b/app/itam/tests/functional/operating_system_version/test_operating_system_version_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py b/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py index 683d6034..f86a2601 100644 --- a/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py +++ b/app/itam/tests/functional/operating_system_version/test_operating_system_version_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/software/test_software_serializer.py b/app/itam/tests/functional/software/test_software_serializer.py index e73a3e2f..80fe8fa3 100644 --- a/app/itam/tests/functional/software/test_software_serializer.py +++ b/app/itam/tests/functional/software/test_software_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/software/test_software_viewset.py b/app/itam/tests/functional/software/test_software_viewset.py index a8c2aa26..bdc83573 100644 --- a/app/itam/tests/functional/software/test_software_viewset.py +++ b/app/itam/tests/functional/software/test_software_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/software_category/test_software_category_serializer.py b/app/itam/tests/functional/software_category/test_software_category_serializer.py index 7158a490..d2bff77a 100644 --- a/app/itam/tests/functional/software_category/test_software_category_serializer.py +++ b/app/itam/tests/functional/software_category/test_software_category_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/software_category/test_software_category_viewset.py b/app/itam/tests/functional/software_category/test_software_category_viewset.py index 67284e8a..5b46f1d6 100644 --- a/app/itam/tests/functional/software_category/test_software_category_viewset.py +++ b/app/itam/tests/functional/software_category/test_software_category_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/functional/software_version/test_software_version_serializer.py b/app/itam/tests/functional/software_version/test_software_version_serializer.py index 6a72e2ba..9d9cb813 100644 --- a/app/itam/tests/functional/software_version/test_software_version_serializer.py +++ b/app/itam/tests/functional/software_version/test_software_version_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itam/tests/functional/software_version/test_software_version_viewset.py b/app/itam/tests/functional/software_version/test_software_version_viewset.py index 94361700..5e554f5b 100644 --- a/app/itam/tests/functional/software_version/test_software_version_viewset.py +++ b/app/itam/tests/functional/software_version/test_software_version_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device/test_device_api.py b/app/itam/tests/unit/device/test_device_api.py index eae9868a..e2c88011 100644 --- a/app/itam/tests/unit/device/test_device_api.py +++ b/app/itam/tests/unit/device/test_device_api.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device/test_device_api_v2.py b/app/itam/tests/unit/device/test_device_api_v2.py index a933a232..9b066bfc 100644 --- a/app/itam/tests/unit/device/test_device_api_v2.py +++ b/app/itam/tests/unit/device/test_device_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device/test_device_permission.py b/app/itam/tests/unit/device/test_device_permission.py index c2596ac8..8d8f40e0 100644 --- a/app/itam/tests/unit/device/test_device_permission.py +++ b/app/itam/tests/unit/device/test_device_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device/test_device_permission_api.py b/app/itam/tests/unit/device/test_device_permission_api.py index 7ec91578..0be6e536 100644 --- a/app/itam/tests/unit/device/test_device_permission_api.py +++ b/app/itam/tests/unit/device/test_device_permission_api.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device_model/test_device_model_api_v2.py b/app/itam/tests/unit/device_model/test_device_model_api_v2.py index ee6ba9ac..2e4e22b8 100644 --- a/app/itam/tests/unit/device_model/test_device_model_api_v2.py +++ b/app/itam/tests/unit/device_model/test_device_model_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device_model/test_device_model_permission.py b/app/itam/tests/unit/device_model/test_device_model_permission.py index e41a63e5..a912d2ee 100644 --- a/app/itam/tests/unit/device_model/test_device_model_permission.py +++ b/app/itam/tests/unit/device_model/test_device_model_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py b/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py index 1a2775f6..bebbc805 100644 --- a/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py +++ b/app/itam/tests/unit/device_operating_system/test_device_operating_system_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device_operating_system/test_unit_device_operating_system_model.py b/app/itam/tests/unit/device_operating_system/test_unit_device_operating_system_model.py index 42561375..19cb9ae8 100644 --- a/app/itam/tests/unit/device_operating_system/test_unit_device_operating_system_model.py +++ b/app/itam/tests/unit/device_operating_system/test_unit_device_operating_system_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/itam/tests/unit/device_software/test_device_software_api_v2.py b/app/itam/tests/unit/device_software/test_device_software_api_v2.py index 0583a5aa..b7c33874 100644 --- a/app/itam/tests/unit/device_software/test_device_software_api_v2.py +++ b/app/itam/tests/unit/device_software/test_device_software_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device_software/test_unit_device_software_model.py b/app/itam/tests/unit/device_software/test_unit_device_software_model.py index 6e138eb1..35f8719c 100644 --- a/app/itam/tests/unit/device_software/test_unit_device_software_model.py +++ b/app/itam/tests/unit/device_software/test_unit_device_software_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/itam/tests/unit/device_type/test_device_type_api_v2.py b/app/itam/tests/unit/device_type/test_device_type_api_v2.py index d42a99c1..d154f695 100644 --- a/app/itam/tests/unit/device_type/test_device_type_api_v2.py +++ b/app/itam/tests/unit/device_type/test_device_type_api_v2.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/device_type/test_device_type_permission.py b/app/itam/tests/unit/device_type/test_device_type_permission.py index 896bfc59..c0d74ad0 100644 --- a/app/itam/tests/unit/device_type/test_device_type_permission.py +++ b/app/itam/tests/unit/device_type/test_device_type_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py b/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py index 70e8a764..a0fe57f3 100644 --- a/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py +++ b/app/itam/tests/unit/endpoint_operating_system_installs/test_operating_system_installs_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py b/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py index 7830f9ce..c3cd9afa 100644 --- a/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py +++ b/app/itam/tests/unit/endpoint_software_installs/test_software_installs_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/inventory/test_api_inventory.py b/app/itam/tests/unit/inventory/test_api_inventory.py index 3b254d22..5a5f9ffd 100644 --- a/app/itam/tests/unit/inventory/test_api_inventory.py +++ b/app/itam/tests/unit/inventory/test_api_inventory.py @@ -12,7 +12,7 @@ from django.test.utils import override_settings from unittest.mock import patch -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/inventory/test_inventory_permission_api.py b/app/itam/tests/unit/inventory/test_inventory_permission_api.py index ca826245..37b61e10 100644 --- a/app/itam/tests/unit/inventory/test_inventory_permission_api.py +++ b/app/itam/tests/unit/inventory/test_inventory_permission_api.py @@ -13,7 +13,7 @@ from django.test.utils import override_settings from unittest.mock import patch -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py b/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py index cdccaa0c..63b4a5d6 100644 --- a/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py +++ b/app/itam/tests/unit/operating_system/test_operating_system_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/operating_system/test_operating_system_permission.py b/app/itam/tests/unit/operating_system/test_operating_system_permission.py index 37fd8b93..94a6d1b7 100644 --- a/app/itam/tests/unit/operating_system/test_operating_system_permission.py +++ b/app/itam/tests/unit/operating_system/test_operating_system_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py b/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py index e1575e76..42e8e8ce 100644 --- a/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py +++ b/app/itam/tests/unit/operating_system_version/test_operating_system_version_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_model.py b/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_model.py index ffc3c6c6..f86e2d37 100644 --- a/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_model.py +++ b/app/itam/tests/unit/operating_system_version/test_unit_operating_system_version_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/itam/tests/unit/operating_system_version_notes/test_unit_operating_system_version_notes_model.py b/app/itam/tests/unit/operating_system_version_notes/test_unit_operating_system_version_notes_model.py index a94e490d..c80439a3 100644 --- a/app/itam/tests/unit/operating_system_version_notes/test_unit_operating_system_version_notes_model.py +++ b/app/itam/tests/unit/operating_system_version_notes/test_unit_operating_system_version_notes_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.tests.unit.model_notes.test_unit_model_notes_model import ( ModelNotesInheritedCases diff --git a/app/itam/tests/unit/software/test_software_api.py b/app/itam/tests/unit/software/test_software_api.py index e40214d8..44c13fce 100644 --- a/app/itam/tests/unit/software/test_software_api.py +++ b/app/itam/tests/unit/software/test_software_api.py @@ -10,7 +10,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/software/test_software_api_v2.py b/app/itam/tests/unit/software/test_software_api_v2.py index 091d3eee..3e7b0b7e 100644 --- a/app/itam/tests/unit/software/test_software_api_v2.py +++ b/app/itam/tests/unit/software/test_software_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/software/test_software_permission.py b/app/itam/tests/unit/software/test_software_permission.py index 337d86bb..59138117 100644 --- a/app/itam/tests/unit/software/test_software_permission.py +++ b/app/itam/tests/unit/software/test_software_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/software/test_software_permission_api.py b/app/itam/tests/unit/software/test_software_permission_api.py index 878b69f5..95edf776 100644 --- a/app/itam/tests/unit/software/test_software_permission_api.py +++ b/app/itam/tests/unit/software/test_software_permission_api.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/software_category/test_software_category_api_v2.py b/app/itam/tests/unit/software_category/test_software_category_api_v2.py index 3314f86c..ba86f2a3 100644 --- a/app/itam/tests/unit/software_category/test_software_category_api_v2.py +++ b/app/itam/tests/unit/software_category/test_software_category_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/software_category/test_software_category_permission.py b/app/itam/tests/unit/software_category/test_software_category_permission.py index 738fe1be..fa2b275f 100644 --- a/app/itam/tests/unit/software_category/test_software_category_permission.py +++ b/app/itam/tests/unit/software_category/test_software_category_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/software_version/test_software_version_api_v2.py b/app/itam/tests/unit/software_version/test_software_version_api_v2.py index aea0a9d9..64f48f1b 100644 --- a/app/itam/tests/unit/software_version/test_software_version_api_v2.py +++ b/app/itam/tests/unit/software_version/test_software_version_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itam/tests/unit/software_version/test_unit_software_version_model.py b/app/itam/tests/unit/software_version/test_unit_software_version_model.py index 8637b197..7abe7675 100644 --- a/app/itam/tests/unit/software_version/test_unit_software_version_model.py +++ b/app/itam/tests/unit/software_version/test_unit_software_version_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/itam/tests/unit/software_version_notes/test_unit_software_version_notes_model.py b/app/itam/tests/unit/software_version_notes/test_unit_software_version_notes_model.py index 06cb6a45..3151415f 100644 --- a/app/itam/tests/unit/software_version_notes/test_unit_software_version_notes_model.py +++ b/app/itam/tests/unit/software_version_notes/test_unit_software_version_notes_model.py @@ -1,7 +1,7 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.tests.unit.model_notes.test_unit_model_notes_model import ( ModelNotesInheritedCases diff --git a/app/itam/views/device.py b/app/itam/views/device.py index 03bd25fb..efabddf9 100644 --- a/app/itam/views/device.py +++ b/app/itam/views/device.py @@ -6,7 +6,7 @@ from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.decorators import method_decorator -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from ..models.device import Device, DeviceSoftware, DeviceOperatingSystem from ..models.software import Software diff --git a/app/itim/migrations/0010_alter_cluster_organization_and_more.py b/app/itim/migrations/0010_alter_cluster_organization_and_more.py new file mode 100644 index 00000000..37e1dcfd --- /dev/null +++ b/app/itim/migrations/0010_alter_cluster_organization_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('itim', '0009_slmticket_requestticket'), + ] + + operations = [ + migrations.AlterField( + model_name='cluster', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='clustertype', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='port', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='service', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/itim/serializers/cluster.py b/app/itim/serializers/cluster.py index d19f1456..c1a813df 100644 --- a/app/itim/serializers/cluster.py +++ b/app/itim/serializers/cluster.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -147,6 +147,6 @@ class ClusterViewSerializer(ClusterModelSerializer): nodes = DeviceBaseSerializer( many = True, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) parent_cluster = ClusterBaseSerializer( many = False, read_only = True ) diff --git a/app/itim/serializers/cluster_type.py b/app/itim/serializers/cluster_type.py index 09aba710..c3c56d88 100644 --- a/app/itim/serializers/cluster_type.py +++ b/app/itim/serializers/cluster_type.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -79,4 +79,4 @@ class ClusterTypeModelSerializer( class ClusterTypeViewSerializer(ClusterTypeModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/itim/serializers/port.py b/app/itim/serializers/port.py index 928f8fde..9817c536 100644 --- a/app/itim/serializers/port.py +++ b/app/itim/serializers/port.py @@ -1,7 +1,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -85,4 +85,4 @@ class PortModelSerializer( class PortViewSerializer(PortModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/itim/serializers/service.py b/app/itim/serializers/service.py index fb0c1e0f..a29865aa 100644 --- a/app/itim/serializers/service.py +++ b/app/itim/serializers/service.py @@ -2,7 +2,7 @@ from rest_framework.fields import empty from rest_framework import serializers from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -283,7 +283,7 @@ class ServiceViewSerializer(ServiceModelSerializer): dependent_service = ServiceBaseSerializer( many = True, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) port = PortBaseSerializer( many = True, read_only = True ) diff --git a/app/itim/tests/functional/cluster/test_cluster_serializer.py b/app/itim/tests/functional/cluster/test_cluster_serializer.py index ca7891f7..1acf40d7 100644 --- a/app/itim/tests/functional/cluster/test_cluster_serializer.py +++ b/app/itim/tests/functional/cluster/test_cluster_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itim/tests/functional/cluster/test_cluster_viewset.py b/app/itim/tests/functional/cluster/test_cluster_viewset.py index e476d7e5..bb5362b9 100644 --- a/app/itim/tests/functional/cluster/test_cluster_viewset.py +++ b/app/itim/tests/functional/cluster/test_cluster_viewset.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/functional/cluster_types/test_cluster_type_serializer.py b/app/itim/tests/functional/cluster_types/test_cluster_type_serializer.py index 0948d355..96d34934 100644 --- a/app/itim/tests/functional/cluster_types/test_cluster_type_serializer.py +++ b/app/itim/tests/functional/cluster_types/test_cluster_type_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from itim.serializers.cluster_type import ClusterType, ClusterTypeModelSerializer diff --git a/app/itim/tests/functional/cluster_types/test_cluster_type_viewset.py b/app/itim/tests/functional/cluster_types/test_cluster_type_viewset.py index 11db1262..dd58a59e 100644 --- a/app/itim/tests/functional/cluster_types/test_cluster_type_viewset.py +++ b/app/itim/tests/functional/cluster_types/test_cluster_type_viewset.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/functional/port/test_port_serializer.py b/app/itim/tests/functional/port/test_port_serializer.py index 09abe75a..48fd5722 100644 --- a/app/itim/tests/functional/port/test_port_serializer.py +++ b/app/itim/tests/functional/port/test_port_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itim/tests/functional/port/test_port_viewset.py b/app/itim/tests/functional/port/test_port_viewset.py index dc9e5242..1a458f86 100644 --- a/app/itim/tests/functional/port/test_port_viewset.py +++ b/app/itim/tests/functional/port/test_port_viewset.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/functional/service/test_service_serializer.py b/app/itim/tests/functional/service/test_service_serializer.py index 5c602fe8..6a0c289b 100644 --- a/app/itim/tests/functional/service/test_service_serializer.py +++ b/app/itim/tests/functional/service/test_service_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/itim/tests/functional/service/test_service_viewset.py b/app/itim/tests/functional/service/test_service_viewset.py index 5bee1956..e2b4490f 100644 --- a/app/itim/tests/functional/service/test_service_viewset.py +++ b/app/itim/tests/functional/service/test_service_viewset.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission,User from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/functional/service_cluster/test_service_cluster_viewset.py b/app/itim/tests/functional/service_cluster/test_service_cluster_viewset.py index 9c2a9ea0..d3474550 100644 --- a/app/itim/tests/functional/service_cluster/test_service_cluster_viewset.py +++ b/app/itim/tests/functional/service_cluster/test_service_cluster_viewset.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/functional/service_device/test_service_device_viewset.py b/app/itim/tests/functional/service_device/test_service_device_viewset.py index ca4fdabc..88f10c9c 100644 --- a/app/itim/tests/functional/service_device/test_service_device_viewset.py +++ b/app/itim/tests/functional/service_device/test_service_device_viewset.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/cluster/test_cluster_api_v2.py b/app/itim/tests/unit/cluster/test_cluster_api_v2.py index 8e170534..b94a8f95 100644 --- a/app/itim/tests/unit/cluster/test_cluster_api_v2.py +++ b/app/itim/tests/unit/cluster/test_cluster_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/cluster/test_cluster_permission.py b/app/itim/tests/unit/cluster/test_cluster_permission.py index ed861874..8bd4d725 100644 --- a/app/itim/tests/unit/cluster/test_cluster_permission.py +++ b/app/itim/tests/unit/cluster/test_cluster_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/cluster_types/test_cluster_type_api_v2.py b/app/itim/tests/unit/cluster_types/test_cluster_type_api_v2.py index 2f223118..ad1a7981 100644 --- a/app/itim/tests/unit/cluster_types/test_cluster_type_api_v2.py +++ b/app/itim/tests/unit/cluster_types/test_cluster_type_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/cluster_types/test_cluster_type_permission.py b/app/itim/tests/unit/cluster_types/test_cluster_type_permission.py index a819e229..2790a5ea 100644 --- a/app/itim/tests/unit/cluster_types/test_cluster_type_permission.py +++ b/app/itim/tests/unit/cluster_types/test_cluster_type_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/port/test_port_api_v2.py b/app/itim/tests/unit/port/test_port_api_v2.py index 50c9da0c..72143adc 100644 --- a/app/itim/tests/unit/port/test_port_api_v2.py +++ b/app/itim/tests/unit/port/test_port_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/port/test_port_permission.py b/app/itim/tests/unit/port/test_port_permission.py index 2d5c5d1e..f3f83a54 100644 --- a/app/itim/tests/unit/port/test_port_permission.py +++ b/app/itim/tests/unit/port/test_port_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/port_notes/test_unit_port_notes_model.py b/app/itim/tests/unit/port_notes/test_unit_port_notes_model.py index afa3507f..e0c1e014 100644 --- a/app/itim/tests/unit/port_notes/test_unit_port_notes_model.py +++ b/app/itim/tests/unit/port_notes/test_unit_port_notes_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.tests.unit.model_notes.test_unit_model_notes_model import ( ModelNotesInheritedCases diff --git a/app/itim/tests/unit/service/test_service_api_v2.py b/app/itim/tests/unit/service/test_service_api_v2.py index 853d57e5..6dbbeed6 100644 --- a/app/itim/tests/unit/service/test_service_api_v2.py +++ b/app/itim/tests/unit/service/test_service_api_v2.py @@ -8,7 +8,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/itim/tests/unit/service/test_service_permission.py b/app/itim/tests/unit/service/test_service_permission.py index 26f12cfe..e3e499e5 100644 --- a/app/itim/tests/unit/service/test_service_permission.py +++ b/app/itim/tests/unit/service/test_service_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/migrations/0006_alter_project_organization_and_more.py b/app/project_management/migrations/0006_alter_project_organization_and_more.py new file mode 100644 index 00000000..ee4640c8 --- /dev/null +++ b/app/project_management/migrations/0006_alter_project_organization_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('project_management', '0005_projecthistory_projectmilestonehistory_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='projectmilestone', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='projectstate', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='projecttype', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + ] diff --git a/app/project_management/serializers/project.py b/app/project_management/serializers/project.py index 33268701..b9149392 100644 --- a/app/project_management/serializers/project.py +++ b/app/project_management/serializers/project.py @@ -1,7 +1,7 @@ from rest_framework import serializers from rest_framework.reverse import reverse -from access.serializers.organization import Organization, OrganizationBaseSerializer +from access.serializers.organization import Organization, TenantBaseSerializer from api.serializers import common from app.serializers.user import UserBaseSerializer @@ -147,7 +147,7 @@ class ProjectViewSerializer(ProjectModelSerializer): manager_user = UserBaseSerializer( many = False, read_only = True ) - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) state = ProjectStateBaseSerializer( many = False, read_only = True ) diff --git a/app/project_management/serializers/project_milestone.py b/app/project_management/serializers/project_milestone.py index 8a72741d..b40e13f9 100644 --- a/app/project_management/serializers/project_milestone.py +++ b/app/project_management/serializers/project_milestone.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.fields import empty from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -111,6 +111,6 @@ class ProjectMilestoneModelSerializer( class ProjectMilestoneViewSerializer(ProjectMilestoneModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) project = ProjectBaseSerializer( many = False, read_only = True ) diff --git a/app/project_management/serializers/project_states.py b/app/project_management/serializers/project_states.py index 0da7fb85..c4a19e03 100644 --- a/app/project_management/serializers/project_states.py +++ b/app/project_management/serializers/project_states.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.fields import empty from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -83,6 +83,6 @@ class ProjectStateModelSerializer( class ProjectStateViewSerializer(ProjectStateModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) runbook = KnowledgeBaseBaseSerializer( many = False, read_only = True ) diff --git a/app/project_management/serializers/project_type.py b/app/project_management/serializers/project_type.py index b1b1b673..15a2c030 100644 --- a/app/project_management/serializers/project_type.py +++ b/app/project_management/serializers/project_type.py @@ -2,7 +2,7 @@ from rest_framework import serializers from rest_framework.fields import empty from rest_framework.reverse import reverse -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -88,6 +88,6 @@ class ProjectTypeModelSerializer( class ProjectTypeViewSerializer(ProjectTypeModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) runbook = KnowledgeBaseBaseSerializer( many = False, read_only = True ) diff --git a/app/project_management/tests/functional/project/test_project_serializer.py b/app/project_management/tests/functional/project/test_project_serializer.py index 79195420..24c8b98f 100644 --- a/app/project_management/tests/functional/project/test_project_serializer.py +++ b/app/project_management/tests/functional/project/test_project_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from project_management.serializers.project import ( Project, diff --git a/app/project_management/tests/functional/project/test_project_viewset.py b/app/project_management/tests/functional/project/test_project_viewset.py index 852b0538..93a1b6c8 100644 --- a/app/project_management/tests/functional/project/test_project_viewset.py +++ b/app/project_management/tests/functional/project/test_project_viewset.py @@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/functional/project_milestone/test_project_milestone_serializer.py b/app/project_management/tests/functional/project_milestone/test_project_milestone_serializer.py index 6e575293..53b5cff0 100644 --- a/app/project_management/tests/functional/project_milestone/test_project_milestone_serializer.py +++ b/app/project_management/tests/functional/project_milestone/test_project_milestone_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from project_management.serializers.project_milestone import ( Project, diff --git a/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py b/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py index 29bf50d2..d158ea0f 100644 --- a/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py +++ b/app/project_management/tests/functional/project_milestone/test_project_milestone_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/functional/project_state/test_project_state_serializer.py b/app/project_management/tests/functional/project_state/test_project_state_serializer.py index fd51e5cd..9ea8a544 100644 --- a/app/project_management/tests/functional/project_state/test_project_state_serializer.py +++ b/app/project_management/tests/functional/project_state/test_project_state_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/project_management/tests/functional/project_state/test_project_state_viewset.py b/app/project_management/tests/functional/project_state/test_project_state_viewset.py index c63350b2..3d00a54a 100644 --- a/app/project_management/tests/functional/project_state/test_project_state_viewset.py +++ b/app/project_management/tests/functional/project_state/test_project_state_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/functional/project_type/test_project_type_serializer.py b/app/project_management/tests/functional/project_type/test_project_type_serializer.py index 31b9b487..c442921d 100644 --- a/app/project_management/tests/functional/project_type/test_project_type_serializer.py +++ b/app/project_management/tests/functional/project_type/test_project_type_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/project_management/tests/functional/project_type/test_project_type_viewset.py b/app/project_management/tests/functional/project_type/test_project_type_viewset.py index 431734ab..a2e507ec 100644 --- a/app/project_management/tests/functional/project_type/test_project_type_viewset.py +++ b/app/project_management/tests/functional/project_type/test_project_type_viewset.py @@ -5,7 +5,7 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project/test_project_api_v2.py b/app/project_management/tests/unit/project/test_project_api_v2.py index 9543410f..ffe451fd 100644 --- a/app/project_management/tests/unit/project/test_project_api_v2.py +++ b/app/project_management/tests/unit/project/test_project_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project/test_project_permission.py b/app/project_management/tests/unit/project/test_project_permission.py index 2706c59d..a1430484 100644 --- a/app/project_management/tests/unit/project/test_project_permission.py +++ b/app/project_management/tests/unit/project/test_project_permission.py @@ -9,7 +9,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project/test_project_permission_api.py b/app/project_management/tests/unit/project/test_project_permission_api.py index 4093e9ce..a51d6095 100644 --- a/app/project_management/tests/unit/project/test_project_permission_api.py +++ b/app/project_management/tests/unit/project/test_project_permission_api.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py b/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py index 4da82091..f4743f82 100644 --- a/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py +++ b/app/project_management/tests/unit/project_milestone/test_project_milestone_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py index 58f155a4..bdf64fce 100644 --- a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py +++ b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py index 8170cff0..1c4999b4 100644 --- a/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py +++ b/app/project_management/tests/unit/project_milestone/test_project_milestone_permission_api.py @@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import Client, TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_milestone/test_unit_projectmilestone_model.py b/app/project_management/tests/unit/project_milestone/test_unit_projectmilestone_model.py index a37acdba..c96d3d3e 100644 --- a/app/project_management/tests/unit/project_milestone/test_unit_projectmilestone_model.py +++ b/app/project_management/tests/unit/project_milestone/test_unit_projectmilestone_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.unit.test_unit_models import ( TenancyObjectInheritedCases diff --git a/app/project_management/tests/unit/project_milestone_notes/test_unit_project_milestone_notes_model.py b/app/project_management/tests/unit/project_milestone_notes/test_unit_project_milestone_notes_model.py index 501a1aec..cbdfa140 100644 --- a/app/project_management/tests/unit/project_milestone_notes/test_unit_project_milestone_notes_model.py +++ b/app/project_management/tests/unit/project_milestone_notes/test_unit_project_milestone_notes_model.py @@ -1,6 +1,6 @@ from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from core.tests.unit.model_notes.test_unit_model_notes_model import ( ModelNotesInheritedCases diff --git a/app/project_management/tests/unit/project_state/test_project_state_api_v2.py b/app/project_management/tests/unit/project_state/test_project_state_api_v2.py index 25845206..ef2cc012 100644 --- a/app/project_management/tests/unit/project_state/test_project_state_api_v2.py +++ b/app/project_management/tests/unit/project_state/test_project_state_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_state/test_project_state_permission.py b/app/project_management/tests/unit/project_state/test_project_state_permission.py index 9aa2145e..c30e7a86 100644 --- a/app/project_management/tests/unit/project_state/test_project_state_permission.py +++ b/app/project_management/tests/unit/project_state/test_project_state_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_state/test_project_state_permission_api.py b/app/project_management/tests/unit/project_state/test_project_state_permission_api.py index dd1716b4..25e9b65e 100644 --- a/app/project_management/tests/unit/project_state/test_project_state_permission_api.py +++ b/app/project_management/tests/unit/project_state/test_project_state_permission_api.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_type/test_project_type_api_v2.py b/app/project_management/tests/unit/project_type/test_project_type_api_v2.py index 9f4617ef..19fd7f89 100644 --- a/app/project_management/tests/unit/project_type/test_project_type_api_v2.py +++ b/app/project_management/tests/unit/project_type/test_project_type_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_type/test_project_type_permission.py b/app/project_management/tests/unit/project_type/test_project_type_permission.py index 75ec40de..1308760d 100644 --- a/app/project_management/tests/unit/project_type/test_project_type_permission.py +++ b/app/project_management/tests/unit/project_type/test_project_type_permission.py @@ -10,7 +10,7 @@ import pytest import unittest import requests -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/project_management/tests/unit/project_type/test_project_type_permission_api.py b/app/project_management/tests/unit/project_type/test_project_type_permission_api.py index f689bffe..c40b07ad 100644 --- a/app/project_management/tests/unit/project_type/test_project_type_permission_api.py +++ b/app/project_management/tests/unit/project_type/test_project_type_permission_api.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/forms/admin_settings_global.py b/app/settings/forms/admin_settings_global.py index c5bd00e0..dfe86e6e 100644 --- a/app/settings/forms/admin_settings_global.py +++ b/app/settings/forms/admin_settings_global.py @@ -1,7 +1,7 @@ from django import forms from django.db.models import Q -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team_user import TeamUsers from core.forms.common import CommonModelForm diff --git a/app/settings/forms/app_settings.py b/app/settings/forms/app_settings.py index 83f19782..bda12b01 100644 --- a/app/settings/forms/app_settings.py +++ b/app/settings/forms/app_settings.py @@ -1,7 +1,7 @@ from django import forms from django.db.models import Q -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team_user import TeamUsers from core.forms.common import CommonModelForm diff --git a/app/settings/forms/external_links.py b/app/settings/forms/external_links.py index 648dda8c..945f0382 100644 --- a/app/settings/forms/external_links.py +++ b/app/settings/forms/external_links.py @@ -2,7 +2,7 @@ from django import forms from django.urls import reverse -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team_user import TeamUsers from app import settings diff --git a/app/settings/forms/user_settings.py b/app/settings/forms/user_settings.py index fca139ac..2fc01100 100644 --- a/app/settings/forms/user_settings.py +++ b/app/settings/forms/user_settings.py @@ -1,7 +1,7 @@ from django import forms from django.db.models import Q -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team_user import TeamUsers from core.forms.common import CommonModelForm diff --git a/app/settings/migrations/0012_alter_appsettings_global_organization_and_more.py b/app/settings/migrations/0012_alter_appsettings_global_organization_and_more.py new file mode 100644 index 00000000..028d1267 --- /dev/null +++ b/app/settings/migrations/0012_alter_appsettings_global_organization_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.9 on 2025-05-14 13:48 + +import access.models.tenancy +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ('settings', '0011_appsettingshistory_externallinkhistory'), + ] + + operations = [ + migrations.AlterField( + model_name='appsettings', + name='global_organization', + field=models.ForeignKey(blank=True, default=None, help_text='Tenant global items will be created in', null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='global_organization', to='access.tenant', verbose_name='Global Tenant'), + ), + migrations.AlterField( + model_name='appsettings', + name='owner_organization', + field=models.ForeignKey(blank=True, default=None, help_text='Tenant the settings belong to', null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='owner_organization', to='access.tenant'), + ), + migrations.AlterField( + model_name='externallink', + name='organization', + field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), + ), + migrations.AlterField( + model_name='usersettings', + name='default_organization', + field=models.ForeignKey(blank=True, default=None, help_text='Users default Tenant', null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='access.tenant', verbose_name='Default Tenant'), + ), + ] diff --git a/app/settings/models/app_settings.py b/app/settings/models/app_settings.py index cbb18e36..acaa61d9 100644 --- a/app/settings/models/app_settings.py +++ b/app/settings/models/app_settings.py @@ -3,7 +3,7 @@ from django.db import models from rest_framework.reverse import reverse from access.fields import * -from access.models.organization import Organization +from access.models.tenant import Tenant from core.lib.feature_not_used import FeatureNotUsed from core.mixin.history_save import SaveHistory @@ -56,9 +56,9 @@ class AppSettings(AppSettingsCommonFields, SaveHistory): owner_organization = models.ForeignKey( - Organization, + Tenant, blank= True, - help_text = 'Organization the settings belong to', + help_text = 'Tenant the settings belong to', default = None, null = True, on_delete=models.SET_DEFAULT, @@ -101,14 +101,14 @@ class AppSettings(AppSettingsCommonFields, SaveHistory): ) global_organization = models.ForeignKey( - Organization, + Tenant, on_delete=models.SET_DEFAULT, blank= True, default = None, - help_text = 'Organization global items will be created in', + help_text = 'Tenant global items will be created in', null = True, related_name = 'global_organization', - verbose_name = 'Global Organization' + verbose_name = 'Global Tenant' ) table_fields: list = [] diff --git a/app/settings/models/user_settings.py b/app/settings/models/user_settings.py index 87d59fd2..fc076b8a 100644 --- a/app/settings/models/user_settings.py +++ b/app/settings/models/user_settings.py @@ -9,7 +9,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from access.fields import * -from access.models.organization import Organization +from access.models.tenant import Tenant from core.lib.feature_not_used import FeatureNotUsed @@ -83,13 +83,13 @@ class UserSettings(UserSettingsCommonFields): ) default_organization = models.ForeignKey( - Organization, + Tenant, blank= True, default = None, - help_text = 'Users default Organization', + help_text = 'Users default Tenant', null = True, on_delete=models.SET_DEFAULT, - verbose_name = 'Default Organization' + verbose_name = 'Default Tenant' ) timezone = models.CharField( diff --git a/app/settings/serializers/app_settings.py b/app/settings/serializers/app_settings.py index 41e20e7a..c16b6ab0 100644 --- a/app/settings/serializers/app_settings.py +++ b/app/settings/serializers/app_settings.py @@ -2,7 +2,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from settings.models.app_settings import AppSettings @@ -85,4 +85,4 @@ class AppSettingsModelSerializer(AppSettingsBaseSerializer): class AppSettingsViewSerializer(AppSettingsModelSerializer): - global_organization = OrganizationBaseSerializer( many = False, read_only = True ) + global_organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/settings/serializers/external_links.py b/app/settings/serializers/external_links.py index 5861ebda..356cf16b 100644 --- a/app/settings/serializers/external_links.py +++ b/app/settings/serializers/external_links.py @@ -2,7 +2,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from api.serializers import common @@ -83,4 +83,4 @@ class ExternalLinkModelSerializer( class ExternalLinkViewSerializer(ExternalLinkModelSerializer): - organization = OrganizationBaseSerializer( many = False, read_only = True ) + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/settings/serializers/user_settings.py b/app/settings/serializers/user_settings.py index 48ee6d55..22093bed 100644 --- a/app/settings/serializers/user_settings.py +++ b/app/settings/serializers/user_settings.py @@ -2,7 +2,7 @@ from rest_framework.reverse import reverse from rest_framework import serializers -from access.serializers.organization import OrganizationBaseSerializer +from access.serializers.organization import TenantBaseSerializer from settings.models.user_settings import UserSettings @@ -87,4 +87,4 @@ class UserSettingsModelSerializer(UserSettingsBaseSerializer): class UserSettingsViewSerializer(UserSettingsModelSerializer): - default_organization = OrganizationBaseSerializer( many = False, read_only = True ) + default_organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/settings/tests/functional/app_settings/test_app_settings_viewset.py b/app/settings/tests/functional/app_settings/test_app_settings_viewset.py index e1c93975..2e0e95db 100644 --- a/app/settings/tests/functional/app_settings/test_app_settings_viewset.py +++ b/app/settings/tests/functional/app_settings/test_app_settings_viewset.py @@ -7,7 +7,7 @@ from django.shortcuts import reverse from django.test import Client, TestCase from django import urls -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/functional/external_links/test_external_link_viewset.py b/app/settings/tests/functional/external_links/test_external_link_viewset.py index dcc392c8..fe283a3f 100644 --- a/app/settings/tests/functional/external_links/test_external_link_viewset.py +++ b/app/settings/tests/functional/external_links/test_external_link_viewset.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import AnonymousUser, Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/functional/external_links/test_external_links_serializer.py b/app/settings/tests/functional/external_links/test_external_links_serializer.py index ab584bc3..26c8cf81 100644 --- a/app/settings/tests/functional/external_links/test_external_links_serializer.py +++ b/app/settings/tests/functional/external_links/test_external_links_serializer.py @@ -4,7 +4,7 @@ from django.test import TestCase from rest_framework.exceptions import ValidationError -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from app.tests.abstract.mock_view import MockView, User diff --git a/app/settings/tests/functional/user_settings/test_user_settings_viewset.py b/app/settings/tests/functional/user_settings/test_user_settings_viewset.py index 6125b54a..3deb8da2 100644 --- a/app/settings/tests/functional/user_settings/test_user_settings_viewset.py +++ b/app/settings/tests/functional/user_settings/test_user_settings_viewset.py @@ -7,7 +7,7 @@ from django.shortcuts import reverse from django.test import Client, TestCase from django import urls -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py b/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py index 117a6e5e..af93e6cc 100644 --- a/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py +++ b/app/settings/tests/unit/app_settings/test_app_settings_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/unit/app_settings/test_app_settings_permission.py b/app/settings/tests/unit/app_settings/test_app_settings_permission.py index 57ff676a..8e1f4895 100644 --- a/app/settings/tests/unit/app_settings/test_app_settings_permission.py +++ b/app/settings/tests/unit/app_settings/test_app_settings_permission.py @@ -9,7 +9,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/unit/external_links/test_external_links_api_v2.py b/app/settings/tests/unit/external_links/test_external_links_api_v2.py index 68465e88..81a41de4 100644 --- a/app/settings/tests/unit/external_links/test_external_links_api_v2.py +++ b/app/settings/tests/unit/external_links/test_external_links_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/unit/external_links/test_external_links_permission.py b/app/settings/tests/unit/external_links/test_external_links_permission.py index f001c7ae..f609376d 100644 --- a/app/settings/tests/unit/external_links/test_external_links_permission.py +++ b/app/settings/tests/unit/external_links/test_external_links_permission.py @@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py b/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py index cc446a54..f7ca23cf 100644 --- a/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py +++ b/app/settings/tests/unit/user_settings/test_unit_user_settings_model.py @@ -9,7 +9,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py b/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py index 55f13567..d92526bc 100644 --- a/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py +++ b/app/settings/tests/unit/user_settings/test_user_settings_api_v2.py @@ -9,7 +9,7 @@ from django.test import Client, TestCase from rest_framework.relations import Hyperlink -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/app/settings/tests/unit/user_settings/test_user_settings_permissions.py b/app/settings/tests/unit/user_settings/test_user_settings_permissions.py index 3413d5cc..800b2be5 100644 --- a/app/settings/tests/unit/user_settings/test_user_settings_permissions.py +++ b/app/settings/tests/unit/user_settings/test_user_settings_permissions.py @@ -9,7 +9,7 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import reverse from django.test import TestCase, Client -from access.models.organization import Organization +from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers diff --git a/docs/projects/centurion_erp/index.md b/docs/projects/centurion_erp/index.md index 2406cf32..df910528 100644 --- a/docs/projects/centurion_erp/index.md +++ b/docs/projects/centurion_erp/index.md @@ -51,7 +51,7 @@ Centurion ERP contains the following modules: - [Markdown](./user/core/markdown.md) - - [Multi-Tenant](./user/access/organization.md) + - [Multi-Tenant](./user/access/tenant.md) - [Single Sign-On {SSO}](./user/configuration.md#single-sign-on) diff --git a/docs/projects/centurion_erp/user/access/index.md b/docs/projects/centurion_erp/user/access/index.md index 09c5105a..30be322f 100644 --- a/docs/projects/centurion_erp/user/access/index.md +++ b/docs/projects/centurion_erp/user/access/index.md @@ -6,14 +6,14 @@ template: project.html about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp --- -The Access module provides the multi-tenancy for this application. Tenancy is organized into organizations, which contain teams which contain users. As part of this module, application permission checking is also conducted. +The Access module provides the multi-tenancy for this application. Tenancy is organized into tenants, which contain teams which contain users. As part of this module, application permission checking is also conducted. ## Components - [Contact / Corporate Directory](./contact.md) -- [Organization](./organization.md) +- [Tenant](./tenant.md) - [Roles](./role.md) @@ -22,10 +22,10 @@ The Access module provides the multi-tenancy for this application. Tenancy is or ## Permission System -The permission system within Centurion ERP is custom and built upon Django's core permission types: add, change, delete and view. For a user to be granted access to perform an action, they must be assigned the permission and have that permission assigned to them as part of the organization they are performing the action in. ALL assigned permissions are limited to the organization the permission is assigned. +The permission system within Centurion ERP is custom and built upon Django's core permission types: add, change, delete and view. For a user to be granted access to perform an action, they must be assigned the permission and have that permission assigned to them as part of the tenant they are performing the action in. ALL assigned permissions are limited to the tenant the permission is assigned. !!! tip - User `A` is in organization `A` and has device view permission. User `A` can view devices in Organization `A` **ONLY**. User `A` although they have the device view permission, can **not** view devices in organization `B`. For User `A` to view devices in organization `B` they would also require the device view permission be assigned to them within organization `B`. + User `A` is in tenant `A` and has device view permission. User `A` can view devices in Organization `A` **ONLY**. User `A` although they have the device view permission, can **not** view devices in tenant `B`. For User `A` to view devices in tenant `B` they would also require the device view permission be assigned to them within tenant `B`. Unlike filesystem based permssions, Centurion ERP permissions are not inclusive, they are mutually exclusive. That is: @@ -42,7 +42,7 @@ The exclusitvity is that each of the permissions listed above, dont include an a ### Gloabl Organization -If the webmaster has setup Centurion ERP to have a [global organization](../settings/app_settings.md#global-organization), as long as the user has the a `view` permission for the model in question in **any** organization, they will be able to view that item within the global organization. This is not the same for the other permissions: `add`, `change` and `delete`. To which they must be granted those permissions within the global organization exclusively. +If the webmaster has setup Centurion ERP to have a [global tenant](../settings/app_settings.md#global-tenant), as long as the user has the a `view` permission for the model in question in **any** tenant, they will be able to view that item within the global tenant. This is not the same for the other permissions: `add`, `change` and `delete`. To which they must be granted those permissions within the global tenant exclusively. !!! tip - User `A` is in organization `A` and the webmaster has setup Centurion to use organization `B` as the global organization. If user `A` has been granted permission `itam.view_software` in organization `A` they will be able to view software within both organization `A` and `B`. + User `A` is in tenant `A` and the webmaster has setup Centurion to use tenant `B` as the global tenant. If user `A` has been granted permission `itam.view_software` in tenant `A` they will be able to view software within both tenant `A` and `B`. diff --git a/docs/projects/centurion_erp/user/access/organization.md b/docs/projects/centurion_erp/user/access/organization.md deleted file mode 100644 index b176ac03..00000000 --- a/docs/projects/centurion_erp/user/access/organization.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Organization -description: Organization Documentation for Centurion ERP by No Fuss Computing -date: 2024-06-17 -template: project.html -about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp ---- - -An organization is how multi-tenancy is conducted within this application. All data within the application is tied to an organization and only users whom are members of the organization with the correct permission can view that item within an organization. - -!!! warning - Any object within any organization that has been marked as `global`. Any user whom has the correct `view` permission will be able to see the global object. - - _**Note:**: This does not include other items that may be attached to the global object that is itself not marked as global._ - - -## Organization Manager - -An organization manager is to be viewed as the "owner" of an organization. With the exception of editing the organization itself, the manager can conduct **ALL** operations against an organization regardless of their permissions. An orgnization manager does not need any permissions to add, change delete or view a `Team` or `Team User`. This also includes not requiring the `view` permission for an `Organization`. diff --git a/docs/projects/centurion_erp/user/access/team.md b/docs/projects/centurion_erp/user/access/team.md index 5cbbea81..809d00bb 100644 --- a/docs/projects/centurion_erp/user/access/team.md +++ b/docs/projects/centurion_erp/user/access/team.md @@ -6,9 +6,9 @@ template: project.html about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp --- -A Team is subordinate to an organization and is a way of grouping permissions with users. A team as the name implies is a collection, in this case contains application users-whom once assigned to a team will be granted the permissions of that team. Permission assigned are an _"allowed"_ to conduct that action. It is not possible to add deny permissions +A Team is subordinate to an tenant and is a way of grouping permissions with users. A team as the name implies is a collection, in this case contains application users-whom once assigned to a team will be granted the permissions of that team. Permission assigned are an _"allowed"_ to conduct that action. It is not possible to add deny permissions -Team permission are not application wide, their scope is limited to objects that are a part of the same organization. In addition any object that is marked `is_global` a user with the objects view permission can see that object. +Team permission are not application wide, their scope is limited to objects that are a part of the same tenant. In addition any object that is marked `is_global` a user with the objects view permission can see that object. !!! warning An Organization manager can conduct **ALL** operations against a team regardless of their permissions. diff --git a/docs/projects/centurion_erp/user/access/tenant.md b/docs/projects/centurion_erp/user/access/tenant.md new file mode 100644 index 00000000..a525b80f --- /dev/null +++ b/docs/projects/centurion_erp/user/access/tenant.md @@ -0,0 +1,19 @@ +--- +title: Tenant +description: Tenant Documentation for Centurion ERP by No Fuss Computing +date: 2024-06-17 +template: project.html +about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp +--- + +An tenant is how multi-tenancy is conducted within this application. All data within the application is tied to an tenant and only users whom are members of the tenant with the correct permission can view that item within an tenant. + +!!! warning + Any object within any tenant that has been marked as `global`. Any user whom has the correct `view` permission will be able to see the global object. + + _**Note:**: This does not include other items that may be attached to the global object that is itself not marked as global._ + + +## Tenant Manager + +A tenant manager is to be viewed as the "owner" of an tenant. With the exception of editing the tenant itself, the manager can conduct **ALL** operations against an tenant regardless of their permissions. An orgnization manager does not need any permissions to add, change delete or view a `Team` or `Team User`. This also includes not requiring the `view` permission for an `Tenant`. diff --git a/docs/projects/centurion_erp/user/itim/cluster.md b/docs/projects/centurion_erp/user/itim/cluster.md index 64cb0190..f99f1f41 100644 --- a/docs/projects/centurion_erp/user/itim/cluster.md +++ b/docs/projects/centurion_erp/user/itim/cluster.md @@ -28,7 +28,7 @@ Within the services the following fields are available: - Name _name of the cluster_ -- [Organization](../access/organization.md) _organization this cluster belongs to_ +- [Tenant](../access/tenant.md) _tenant this cluster belongs to_ - [Nodes](../itam/device.md) _Cluster Nodes_ diff --git a/mkdocs.yml b/mkdocs.yml index e4aaa8c4..332bc0e1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -169,12 +169,12 @@ nav: - projects/centurion_erp/user/access/contact.md - - projects/centurion_erp/user/access/organization.md - - projects/centurion_erp/user/access/role.md - projects/centurion_erp/user/access/team.md + - projects/centurion_erp/user/access/tenant.md + - Accounting: - projects/centurion_erp/user/accounting/index.md From 59b4b5ff39edc76e5ed7297c567264dff310c0bb Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 02:45:03 +0930 Subject: [PATCH 130/175] feat(access): Organization -> Tenant Permission Migration ref: #756 #505 --- ..._migrate_organization_permission_tenant.py | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 app/access/migrations/0009_migrate_organization_permission_tenant.py diff --git a/app/access/migrations/0009_migrate_organization_permission_tenant.py b/app/access/migrations/0009_migrate_organization_permission_tenant.py new file mode 100644 index 00000000..999b6b8f --- /dev/null +++ b/app/access/migrations/0009_migrate_organization_permission_tenant.py @@ -0,0 +1,135 @@ + +from django.contrib.auth.models import ContentType, Permission +from django.db import migrations + +from access.models.team import Team + +ContentType.DoesNotExist + +def add_tenancy_permissions(apps, schema_editor): + + print('') + print(f"Begin permission migration for rename of Organization to Tenant.") + + try: + + add_permission = Permission.objects.get( + codename = 'add_tenant', + content_type = ContentType.objects.get( + app_label = 'access', + model = 'tenant', + ) + ) + + change_permission = Permission.objects.get( + codename = 'change_tenant', + content_type = ContentType.objects.get( + app_label = 'access', + model = 'tenant', + ) + ) + + delete_permission = Permission.objects.get( + codename = 'delete_tenant', + content_type = ContentType.objects.get( + app_label = 'access', + model = 'tenant', + ) + ) + + view_permission = Permission.objects.get( + codename = 'view_tenant', + content_type = ContentType.objects.get( + app_label = 'access', + model = 'tenant', + ) + ) + + print(f' Searching for Teams.') + + teams = Team.objects.select_related('group_ptr__permissions') + + print(f'Found {str(len(teams))} Teams.') + + for team in teams: + + print(f' Processing Team {str(team.team_name)}.') + + permissions = team.group_ptr.permissions.all() + + print(f' Searching for Organization Permissions.') + print(f' Found {str(len(permissions))} Permissions.') + + for permission in permissions: + + if '_organization' not in permission.codename: + + continue + + action = str(permission.codename).split('_')[0] + + print(f' Found Organization Permission {str(action)}') + + if action == 'add': + + team.group_ptr.permissions.add( add_permission ) + + print(f' Add Tenant Permission {str(action)}') + + team.group_ptr.permissions.remove( permission ) + + print(f' Remove Organization Permission {str(action)}') + + elif action == 'change': + + team.group_ptr.permissions.add( change_permission ) + + print(f' Add Tenant Permission {str(action)}') + + team.group_ptr.permissions.remove( permission ) + + print(f' Remove Organization Permission {str(action)}') + + elif action == 'delete': + + team.group_ptr.permissions.add( delete_permission ) + + print(f' Add Tenant Permission {str(action)}') + + team.group_ptr.permissions.remove( permission ) + + print(f' Remove Organization Permission {str(action)}') + + elif action == 'view': + + team.group_ptr.permissions.add( view_permission ) + + print(f' Add Tenant Permission {str(action)}') + + team.group_ptr.permissions.remove( permission ) + + print(f' Remove Organization Permission {str(action)}') + + + print(f' Completed Team {str(team.team_name)}.') + + except ContentType.DoesNotExist: + # DB is new so no content types. no migration to be done. + pass + + print(' Permission Migration Actions Complete.') + + + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), + ] + + operations = [ + migrations.RunPython(add_tenancy_permissions), + ] + + From c3109f18947e8b62a1b490cfdb2787aca8db2a20 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 03:58:37 +0930 Subject: [PATCH 131/175] feat(docker): Serve a robots.txt file for NO indexing ref: #756 --- app/project-static/robots.txt | 2 ++ includes/etc/nginx/conf.d/centurion.conf | 4 ++++ 2 files changed, 6 insertions(+) create mode 100644 app/project-static/robots.txt diff --git a/app/project-static/robots.txt b/app/project-static/robots.txt new file mode 100644 index 00000000..1f53798b --- /dev/null +++ b/app/project-static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/includes/etc/nginx/conf.d/centurion.conf b/includes/etc/nginx/conf.d/centurion.conf index b69b689a..93388b56 100644 --- a/includes/etc/nginx/conf.d/centurion.conf +++ b/includes/etc/nginx/conf.d/centurion.conf @@ -4,6 +4,10 @@ server { location = /favicon.ico { access_log off; log_not_found off; } + location /robots.txt { + alias /app/static/robots.txt; + } + location /static/ { alias /app/static/; From bb8c6378bc0c12b6bb07e55735acc04ec05dbf15 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 04:16:33 +0930 Subject: [PATCH 132/175] chore: Add some tests to aid in planning ref: #756 #758 --- app/access/models/__init__.py | 1 + app/api/models/__init__.py | 1 + app/app/tests/unit/test_preperation_work.py | 133 +++++++++++++++++++ app/devops/models/__init__.py | 5 + app/devops/models/git_repository/__init__.py | 2 + app/itim/models/__init__.py | 2 + app/settings/models/__init__.py | 2 + 7 files changed, 146 insertions(+) create mode 100644 app/app/tests/unit/test_preperation_work.py diff --git a/app/access/models/__init__.py b/app/access/models/__init__.py index da54c1d7..c24760f8 100644 --- a/app/access/models/__init__.py +++ b/app/access/models/__init__.py @@ -1,2 +1,3 @@ from . import contact from . import person +from . import role diff --git a/app/api/models/__init__.py b/app/api/models/__init__.py index e69de29b..6d1aea54 100644 --- a/app/api/models/__init__.py +++ b/app/api/models/__init__.py @@ -0,0 +1 @@ +from . import tokens \ No newline at end of file diff --git a/app/app/tests/unit/test_preperation_work.py b/app/app/tests/unit/test_preperation_work.py new file mode 100644 index 00000000..d60afd43 --- /dev/null +++ b/app/app/tests/unit/test_preperation_work.py @@ -0,0 +1,133 @@ +import pytest + +from django.apps import apps +from django.conf import settings + + +class MetaChecksPyTest: + + @staticmethod + def get_models( excludes: list[ str ] ) -> list[ tuple ]: + """Fetch models from Centurion Apps + + Args: + excludes (list[ str ]): Words that may be in a models name to exclude + + Returns: + list[ tuple ]: Centurion ERP Only models + """ + + models: list = [] + + model_apps: list = [] + + exclude_model_apps = [ + 'django', + 'django_celery_results', + 'django_filters', + 'drf_spectacular', + 'drf_spectacular_sidecar', + 'coresheaders', + 'corsheaders', + 'rest_framework', + 'rest_framework_json_api', + 'social_django', + ] + + for app in settings.INSTALLED_APPS: + + app = app.split('.')[0] + + if app in exclude_model_apps: + continue + + model_apps += [ app ] + + + for model in apps.get_models(): + + if model._meta.app_label not in model_apps: + continue + + skip = False + + for exclude in excludes: + + if exclude in str(model._meta.model_name): + skip = True + break + + if skip: + continue + + models += [ (model,) ] + + return models + + + + notes_models = get_models( [ 'base', 'history', 'note', 'ticket' ] ) + + + @pytest.mark.xfail( reason = 'Test Checks if installed models has a notes table' ) + @pytest.mark.parametrize( + argnames = [ + 'test_model' + ], + argvalues = notes_models, + ids = [ model[0]._meta.app_label + '_' + model[0]._meta.model_name for model in notes_models ] + ) + def test_model_has_notes(self, test_model): + """Note Table check + + Check if the model has a corresponding notes table that should be + called `__notes` + """ + + notes_model_table: str = test_model._meta.app_label + '_' + test_model._meta.model_name + '_notes' + + found = False + + for model in apps.get_models(): + + if model._meta.db_table == notes_model_table: + + found = True + break + + + assert found + + + + history_models = get_models( [ 'base', 'history', 'note', 'ticket' ] ) + + + @pytest.mark.xfail( reason = 'Test Checks if installed models has a History table' ) + @pytest.mark.parametrize( + argnames = [ + 'test_model' + ], + argvalues = history_models, + ids = [ model[0]._meta.app_label + '_' + model[0]._meta.model_name for model in history_models ] + ) + def test_model_has_history(self, test_model): + """History Table check + + Check if the model has a corresponding notes table that should be + called `__notes` + """ + + history_model_table: str = test_model._meta.app_label + '_' + test_model._meta.model_name + '_history' + + found = False + + for model in apps.get_models(): + + if model._meta.db_table == history_model_table: + + found = True + break + + + assert found diff --git a/app/devops/models/__init__.py b/app/devops/models/__init__.py index e69de29b..25d04e1c 100644 --- a/app/devops/models/__init__.py +++ b/app/devops/models/__init__.py @@ -0,0 +1,5 @@ +from . import check_ins +from . import git_group +from . import git_repository +from . import feature_flag +from . import software_enable_feature_flag \ No newline at end of file diff --git a/app/devops/models/git_repository/__init__.py b/app/devops/models/git_repository/__init__.py index e69de29b..c201b515 100644 --- a/app/devops/models/git_repository/__init__.py +++ b/app/devops/models/git_repository/__init__.py @@ -0,0 +1,2 @@ +from . import github +from . import gitlab \ No newline at end of file diff --git a/app/itim/models/__init__.py b/app/itim/models/__init__.py index 7144f125..4e4192e8 100644 --- a/app/itim/models/__init__.py +++ b/app/itim/models/__init__.py @@ -1,2 +1,4 @@ +from . import clusters +from . import services from . import slm_ticket_base from . import request_ticket diff --git a/app/settings/models/__init__.py b/app/settings/models/__init__.py index e69de29b..2cd0ead0 100644 --- a/app/settings/models/__init__.py +++ b/app/settings/models/__init__.py @@ -0,0 +1,2 @@ +from . import external_link +from . import user_settings \ No newline at end of file From 27e42fac560f3f8fed0d62d22ca54362389b4e73 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 17:44:20 +0930 Subject: [PATCH 133/175] feat(access): Organization -> Tenant Permission Migration ref: #761 #505 --- .../0027_alter_ticketlinkeditem_item_type.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app/core/migrations/0027_alter_ticketlinkeditem_item_type.py diff --git a/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py b/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py new file mode 100644 index 00000000..903cd524 --- /dev/null +++ b/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.9 on 2025-05-15 07:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0026_alter_manufacturer_organization_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='ticketlinkeditem', + name='item_type', + field=models.IntegerField(choices=[(1, 'Cluster'), (2, 'Config Group'), (3, 'Device'), (4, 'Operating System'), (5, 'Service'), (6, 'Software'), (7, 'Knowledge Base'), (8, 'Tenant'), (9, 'Team'), (10, 'Feature Flag'), (11, 'Software Version'), (12, 'Ticket Category'), (13, 'Ticket Comment Category'), (14, 'Project State'), (15, 'Git Repository'), (16, 'Entity'), (17, 'Role'), (18, 'Asset'), (19, 'IT Asset')], help_text='Python Model location for linked item', verbose_name='Item Type'), + ), + ] From b79874d0560a940ee88af3877e2b4beed9090ca0 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 17:46:57 +0930 Subject: [PATCH 134/175] chore(access): Update tenant url ref: #761 #505 --- app/api/react_ui_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index 02008bad..b5f9d309 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -396,7 +396,7 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): "display_name": "Tenancy", "name": "tenant", "icon": "organization", - "link": "/access/organization" + "link": "/access/tenant" }, } }, From 34b2571a2e0c85293f04df6ad2124cd104079dbb Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:33:21 +0930 Subject: [PATCH 135/175] feat(access): New model access.Company ref: #761 #760 --- app/access/models/__init__.py | 1 + app/access/models/company_base.py | 102 ++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 app/access/models/company_base.py diff --git a/app/access/models/__init__.py b/app/access/models/__init__.py index c24760f8..de76019a 100644 --- a/app/access/models/__init__.py +++ b/app/access/models/__init__.py @@ -1,3 +1,4 @@ from . import contact +from . import company_base from . import person from . import role diff --git a/app/access/models/company_base.py b/app/access/models/company_base.py new file mode 100644 index 00000000..93a59ee3 --- /dev/null +++ b/app/access/models/company_base.py @@ -0,0 +1,102 @@ +from django.db import models + +from access.models.entity import Entity + + + +class Company( + Entity +): +# This model is intended to be called `Organization`, however at the time of +# creation this was not possible as Tenant (ne Organization) still has +# references in code to `organization` witch clashes with the intended name of +# this model. + + + class Meta: + + ordering = [ + 'name', + ] + + sub_model_type = 'company' + + verbose_name = 'Company' + + verbose_name_plural = 'Companies' + + + name = models.CharField( + blank = False, + help_text = 'The name of this entity', + max_length = 80, + unique = False, + verbose_name = 'Name' + ) + + + def __str__(self) -> str: + + return self.name + + + documentation = '' + + history_model_name = 'company' + + page_layout: dict = [ + { + "name": "Details", + "slug": "details", + "sections": [ + { + "layout": "double", + "left": [ + 'organization', + 'name', + ], + "right": [ + 'model_notes', + 'created', + 'modified', + ] + } + ] + }, + { + "name": "Knowledge Base", + "slug": "kb_articles", + "sections": [ + { + "layout": "table", + "field": "knowledge_base", + } + ] + }, + { + "name": "Tickets", + "slug": "tickets", + "sections": [ + { + "layout": "table", + "field": "tickets", + } + ] + }, + { + "name": "Notes", + "slug": "notes", + "sections": [] + }, + ] + + table_fields: list = [ + 'name', + 'organization', + 'created', + ] + + + def clean(self): + + super().clean() From 312267567fc93f171038b9fb656097885c689b49 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:33:52 +0930 Subject: [PATCH 136/175] feat(access): Serializer for model access.Company ref: #761 #760 --- app/access/serializers/entity_company.py | 70 ++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/access/serializers/entity_company.py diff --git a/app/access/serializers/entity_company.py b/app/access/serializers/entity_company.py new file mode 100644 index 00000000..88f65045 --- /dev/null +++ b/app/access/serializers/entity_company.py @@ -0,0 +1,70 @@ +from drf_spectacular.utils import extend_schema_serializer + +from access.models.company_base import Company + +from access.serializers.entity import ( + BaseSerializer as BaseBaseSerializer, + ModelSerializer as BaseModelSerializer, +) +from access.serializers.organization import TenantBaseSerializer + + + +class BaseSerializer( + BaseBaseSerializer, +): + + pass + + + +@extend_schema_serializer(component_name = 'CompanyEntityModelSerializer') +class ModelSerializer( + BaseSerializer, + BaseModelSerializer, +): + """Company Model + + This model inherits from the Entity base model. + """ + + + class Meta: + + model = Company + + fields = [ + 'id', + 'entity_ptr_id', + 'organization', + 'entity_type', + 'display_name', + 'name', + 'model_notes', + 'is_global', + 'created', + 'modified', + '_urls', + ] + + read_only_fields = [ + 'id', + 'display_name', + 'entity_type', + 'created', + 'modified', + '_urls', + ] + + + +@extend_schema_serializer(component_name = 'CompanyEntityViewSerializer') +class ViewSerializer( + ModelSerializer, +): + """Company View Model + + This model inherits from the Entity base model. + """ + + organization = TenantBaseSerializer(many=False, read_only=True) From 53f99f620cd4d400f27b7b2a3a6d7509bf0d3e75 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:34:03 +0930 Subject: [PATCH 137/175] feat(access): Migration for model access.Company ref: #761 #760 --- app/access/migrations/0010_company.py | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/access/migrations/0010_company.py diff --git a/app/access/migrations/0010_company.py b/app/access/migrations/0010_company.py new file mode 100644 index 00000000..5733a568 --- /dev/null +++ b/app/access/migrations/0010_company.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.9 on 2025-05-15 07:47 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0009_migrate_organization_permission_tenant'), + ] + + operations = [ + migrations.CreateModel( + name='Company', + fields=[ + ('entity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.entity')), + ('name', models.CharField(help_text='The name of this entity', max_length=80, verbose_name='Name')), + ], + options={ + 'verbose_name': 'Company', + 'verbose_name_plural': 'Companies', + 'ordering': ['name'], + 'sub_model_type': 'company', + }, + bases=('access.entity',), + ), + ] From b2d1903009b275feb1fbbbb87d98d11af2185659 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:34:17 +0930 Subject: [PATCH 138/175] feat(access): URL route for model access.Company ref: #761 #760 --- app/api/react_ui_metadata.py | 38 ++++++++++++++++++++++++------------ app/api/urls_v2.py | 13 +++++++----- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/app/api/react_ui_metadata.py b/app/api/react_ui_metadata.py index b5f9d309..29093844 100644 --- a/app/api/react_ui_metadata.py +++ b/app/api/react_ui_metadata.py @@ -587,9 +587,33 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): } }) - if request.feature_flag['2025-00002']: + + if request.feature_flag['2025-00003']: + + nav['access']['pages'].update({ + 'view_role': { + "display_name": "Roles", + "name": "roles", + "icon": 'roles', + "link": "/access/role" + } + }) + + + if request.feature_flag['2025-00008']: + + nav['access']['pages'].update({ + 'view_company': { + "display_name": "Companies", + "name": "organization", + "icon": 'organization', + "link": "/access/company" + } + }) + + nav['access']['pages'].update({ 'view_contact': { "display_name": "Directory", @@ -598,6 +622,7 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): } }) + if request.feature_flag['2025-00005']: nav['human_resources']['pages'].update({ @@ -610,17 +635,6 @@ class ReactUIMetadata(OverRideJSONAPIMetadata): }) - if request.feature_flag['2025-00003']: - - nav['access']['pages'].update({ - 'view_role': { - "display_name": "Roles", - "name": "roles", - "icon": 'roles', - "link": "/access/role" - } - }) - if request.feature_flag['2025-00004']: nav['accounting']['pages'].update({ diff --git a/app/api/urls_v2.py b/app/api/urls_v2.py index 14f3d0b1..6c656864 100644 --- a/app/api/urls_v2.py +++ b/app/api/urls_v2.py @@ -162,15 +162,18 @@ entity_type_names = str(entity_type_names)[:-1] router.register('access', access_v2.Index, basename='_api_v2_access_home') + +router.register('access/(?P[company]+)', entity.ViewSet, feature_flag = '2025-00008', basename='_api_v2_company') + router.register(f'access/entity/(?P[{entity_type_names}]+)?', entity.ViewSet, feature_flag = '2025-00002', basename='_api_v2_entity_sub') router.register('access/entity', entity.NoDocsViewSet, feature_flag = '2025-00002', basename='_api_v2_entity') router.register('access/entity/(?P[0-9]+)/notes', entity_notes.ViewSet, feature_flag = '2025-00002', basename='_api_v2_entity_note') -router.register('access/organization', organization_v2.ViewSet, basename='_api_v2_organization') -router.register('access/organization/(?P[0-9]+)/notes', organization_notes.ViewSet, basename='_api_v2_organization_note') -router.register('access/organization/(?P[0-9]+)/team', team_v2.ViewSet, basename='_api_v2_organization_team') -router.register('access/organization/(?P[0-9]+)/team/(?P[0-9]+)/notes', team_notes.ViewSet, basename='_api_v2_team_note') -router.register('access/organization/(?P[0-9]+)/team/(?P[0-9]+)/user', team_user_v2.ViewSet, basename='_api_v2_organization_team_user') +router.register('access/tenant', organization_v2.ViewSet, basename='_api_v2_organization') +router.register('access/tenant/(?P[0-9]+)/notes', organization_notes.ViewSet, basename='_api_v2_organization_note') +router.register('access/tenant/(?P[0-9]+)/team', team_v2.ViewSet, basename='_api_v2_organization_team') +router.register('access/tenant/(?P[0-9]+)/team/(?P[0-9]+)/notes', team_notes.ViewSet, basename='_api_v2_team_note') +router.register('access/tenant/(?P[0-9]+)/team/(?P[0-9]+)/user', team_user_v2.ViewSet, basename='_api_v2_organization_team_user') router.register('access/role', role.ViewSet, feature_flag = '2025-00003', basename='_api_v2_role') router.register('access/role/(?P[0-9]+)/notes', role_notes.ViewSet, feature_flag = '2025-00003', basename='_api_v2_role_note') From a51a79a76343be6c45485739d032adb2bf16e537 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:34:51 +0930 Subject: [PATCH 139/175] feat(access): model access.Company feature flag `2025-00008` ref: #761 #760 --- app/app/settings.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/app/settings.py b/app/app/settings.py index 59a3c1d4..cb039519 100644 --- a/app/app/settings.py +++ b/app/app/settings.py @@ -685,6 +685,15 @@ if FEATURE_FLAGGING_ENABLED: "created": "", "modified": "" } + }, + { + "2025-00008": { + "name": "access.Company", + "description": "Company Entity Role. See https://github.com/nofusscomputing/centurion_erp/issues/704", + "enabled": True, + "created": "", + "modified": "" + } } ] From b0a6f207cdf79ae4d024e408920e507efd80df75 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 18:40:44 +0930 Subject: [PATCH 140/175] docs(user): Add Conpany page ref: #761 #760 --- docs/projects/centurion_erp/user/access/company.md | 9 +++++++++ docs/projects/centurion_erp/user/access/index.md | 2 ++ mkdocs.yml | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 docs/projects/centurion_erp/user/access/company.md diff --git a/docs/projects/centurion_erp/user/access/company.md b/docs/projects/centurion_erp/user/access/company.md new file mode 100644 index 00000000..8984a625 --- /dev/null +++ b/docs/projects/centurion_erp/user/access/company.md @@ -0,0 +1,9 @@ +--- +title: Company Entity +description: Centurion ERP Company Entity user documentation +date: 2025-04-04 +template: project.html +about: https://github.com/nofusscomputing/centurion_erp +--- + +The Company model is a sub-model of entity. Its purpose is to be a base model for the different types of companies. With this in mind it is also the single location to fetch any type of company, regardless of its actual model name. diff --git a/docs/projects/centurion_erp/user/access/index.md b/docs/projects/centurion_erp/user/access/index.md index 30be322f..94c2c061 100644 --- a/docs/projects/centurion_erp/user/access/index.md +++ b/docs/projects/centurion_erp/user/access/index.md @@ -13,6 +13,8 @@ The Access module provides the multi-tenancy for this application. Tenancy is or - [Contact / Corporate Directory](./contact.md) +- [Company](./company.md) + - [Tenant](./tenant.md) - [Roles](./role.md) diff --git a/mkdocs.yml b/mkdocs.yml index 332bc0e1..922f130a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -169,6 +169,8 @@ nav: - projects/centurion_erp/user/access/contact.md + - projects/centurion_erp/user/access/company.md + - projects/centurion_erp/user/access/role.md - projects/centurion_erp/user/access/team.md From 2e9fe29e99491d2bdc9fa8f262bb25e7b7fde58a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 19:45:04 +0930 Subject: [PATCH 141/175] fix(api): Correct ViewSet Sub-Model lookup ref: #761 --- app/api/viewsets/common.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index bfee9eab..4a5a2629 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -793,7 +793,8 @@ class SubModelViewSet( def related_objects(self, model, model_kwarg): """Recursive relate_objects fetch - Fetch the model that is lowest in the chain of inherited models + Fetch the model where ._meta.sub_model_type matches the + model_kwarg value. Args: model (django.db.models.Model): The model to obtain the @@ -801,13 +802,15 @@ class SubModelViewSet( model_kwarg (str): The URL Kwarg of the model. Returns: - _type_: _description_ + Model: The model for the ViewSet """ related_model = None if model_kwarg: + is_nested_lookup = False + for related_object in model._meta.related_objects: if( @@ -833,9 +836,23 @@ class SubModelViewSet( related_model = self.related_objects(model = related_object.related_model, model_kwarg = model_kwarg) + is_nested_lookup = True - if related_model is None: + if( + str( + related_model._meta.sub_model_type + ).lower().replace(' ', '_') == model_kwarg + ): + + break + + else: + + related_model = None + + + if related_model is None and not is_nested_lookup: related_model = self.base_model From 8e9dca56bc7b9d089b32793172b503d021a292af Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:02:39 +0930 Subject: [PATCH 142/175] refactor(access): Update Model Entity to use PyTest API Fields Render ref: #761 #730 --- app/access/tests/unit/entity/conftest.py | 14 ++ .../entity/test_unit_entity_api_fields.py | 78 +++++++ .../unit/entity/test_unit_entity_api_v2.py | 219 ------------------ 3 files changed, 92 insertions(+), 219 deletions(-) create mode 100644 app/access/tests/unit/entity/conftest.py create mode 100644 app/access/tests/unit/entity/test_unit_entity_api_fields.py delete mode 100644 app/access/tests/unit/entity/test_unit_entity_api_v2.py diff --git a/app/access/tests/unit/entity/conftest.py b/app/access/tests/unit/entity/conftest.py new file mode 100644 index 00000000..319de767 --- /dev/null +++ b/app/access/tests/unit/entity/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from access.models.entity import Entity + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Entity + + yield request.cls.model + + del request.cls.model diff --git a/app/access/tests/unit/entity/test_unit_entity_api_fields.py b/app/access/tests/unit/entity/test_unit_entity_api_fields.py new file mode 100644 index 00000000..4c0a5ff4 --- /dev/null +++ b/app/access/tests/unit/entity/test_unit_entity_api_fields.py @@ -0,0 +1,78 @@ +import pytest + +from access.models.entity import Entity + +from api.tests.unit.test_unit_api_fields import ( + APIFieldsInheritedCases, +) + + + +class EntityAPITestCases( + APIFieldsInheritedCases, +): + + base_model = Entity + + + @pytest.fixture( scope = 'class') + def setup_model(self, request, + model, + ): + + if model != self.base_model: + + request.cls.url_view_kwargs.update({ + 'entity_model': model._meta.sub_model_type, + }) + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_pre, + setup_model, + create_model, + setup_post, + ): + + pass + + + parameterized_test_data = { + 'entity_type': { + 'expected': str + }, + '_urls.history': { + 'expected': str + }, + '_urls.knowledge_base': { + 'expected': str + } + } + + kwargs_create_item: dict = { + 'entity_type': 'entity', + } + + url_ns_name = '_api_v2_entity' + """Url namespace (optional, if not required) and url name""" + + + +class EntityAPIInheritedCases( + EntityAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + url_ns_name = '_api_v2_entity_sub' + + + +class EntityAPIPyTest( + EntityAPITestCases, +): + + pass diff --git a/app/access/tests/unit/entity/test_unit_entity_api_v2.py b/app/access/tests/unit/entity/test_unit_entity_api_v2.py deleted file mode 100644 index aee1c266..00000000 --- a/app/access/tests/unit/entity/test_unit_entity_api_v2.py +++ /dev/null @@ -1,219 +0,0 @@ -import django - -from django.contrib.auth.models import Permission -from django.contrib.contenttypes.models import ContentType -from django.shortcuts import reverse -from django.test import Client, TestCase - -# from rest_framework.relations import Hyperlink - -from access.models.entity import Entity -from access.models.tenant import Tenant as Organization -from access.models.team import Team -from access.models.team_user import TeamUsers - -from api.tests.abstract.api_fields import APITenancyObject - -User = django.contrib.auth.get_user_model() - - - -class APITestCases( - APITenancyObject, -): - - model = None - - kwargs_item_create: dict = None - - url_ns_name = None - """Url namespace (optional, if not required) and url name""" - - - @classmethod - def setUpTestData(self): - """Setup Test - - 1. Create an organization for user and item - 2. Create an item - - """ - - self.organization = Organization.objects.create(name='test_org') - - - self.item = self.model.objects.create( - organization = self.organization, - model_notes = 'random notes', - **self.kwargs_item_create - ) - - - self.url_view_kwargs = { - 'pk': self.item.id - } - - if self.model._meta.model_name != 'entity': - - self.url_view_kwargs.update({ - 'entity_model': self.item.entity_type, - }) - - view_permissions = Permission.objects.get( - codename = 'view_' + self.model._meta.model_name, - content_type = ContentType.objects.get( - app_label = self.model._meta.app_label, - model = self.model._meta.model_name, - ) - ) - - view_team = Team.objects.create( - team_name = 'view_team', - organization = self.organization, - ) - - view_team.permissions.set([view_permissions]) - - self.view_user = User.objects.create_user(username="test_user_view", password="password") - TeamUsers.objects.create( - team = view_team, - user = self.view_user - ) - - client = Client() - url = reverse('v2:' + self.url_ns_name + '-detail', kwargs=self.url_view_kwargs) - - - client.force_login(self.view_user) - response = client.get(url) - - self.api_data = response.data - - - - def test_api_field_exists_entity_type(self): - """ Test for existance of API Field - - entity_type field must exist - """ - - assert 'entity_type' in self.api_data - - - def test_api_field_type_entity_type(self): - """ Test for type for API Field - - entity_type field must be str - """ - - assert type(self.api_data['entity_type']) is str - - - - def test_api_field_exists_url_history(self): - """ Test for existance of API Field - - _urls.history field must exist - """ - - assert 'history' in self.api_data['_urls'] - - - def test_api_field_type_url_history(self): - """ Test for type for API Field - - _urls.history field must be str - """ - - assert type(self.api_data['_urls']['history']) is str - - - def test_api_field_type_url_history_value(self): - """ Test for url value - - _urls.history field must use the endpoint for entity model - """ - - assert str(self.api_data['_urls']['history']).endswith('/' + str(self.item._meta.app_label) + '/' + str(self.item._meta.model_name) + '/' + str(self.item.pk) + '/history') - - - - def test_api_field_exists_url_knowledge_base(self): - """ Test for existance of API Field - - _urls.knowledge_base field must exist - """ - - assert 'knowledge_base' in self.api_data['_urls'] - - - def test_api_field_type_url_knowledge_base(self): - """ Test for type for API Field - - _urls.knowledge_base field must be str - """ - - assert type(self.api_data['_urls']['knowledge_base']) is str - - - def test_api_field_type_url_knowledge_base_value(self): - """ Test for url value - - _urls.knowledge_base field must use the endpoint for entity model - """ - - assert str(self.api_data['_urls']['knowledge_base']).endswith('/assistance/entity/' + str(self.item.pk) + '/knowledge_base') - - - -class EntityAPIInheritedCases( - APITestCases, -): - - kwargs_item_create: dict = None - - model = None - - url_ns_name = '_api_v2_entity_sub' - - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create.update({ - 'entity_type': self.model._meta.model_name - }) - - super().setUpTestData() - - - def test_api_field_exists_entity_value(self): - """ Test for value of API Field - - entity_type field must match model name - """ - - assert self.api_data['entity_type'] == self.model._meta.model_name - - - -class EntityAPITest( - APITestCases, - TestCase, -): - - kwargs_item_create: dict = None - - model = Entity - - url_ns_name = '_api_v2_entity' - - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create = { - 'entity_type': 'entity' - } - - super().setUpTestData() \ No newline at end of file From 08dbe1e35b31f4a52b9a220e6145f20677b47f1b Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:02:50 +0930 Subject: [PATCH 143/175] refactor(access): Update Model Contact to use PyTest API Fields Render ref: #761 #730 --- app/access/tests/unit/contact/conftest.py | 14 +++ .../contact/test_unit_contact_api_fields.py | 40 +++++++++ .../unit/contact/test_unit_contact_api_v2.py | 90 ------------------- 3 files changed, 54 insertions(+), 90 deletions(-) create mode 100644 app/access/tests/unit/contact/conftest.py create mode 100644 app/access/tests/unit/contact/test_unit_contact_api_fields.py delete mode 100644 app/access/tests/unit/contact/test_unit_contact_api_v2.py diff --git a/app/access/tests/unit/contact/conftest.py b/app/access/tests/unit/contact/conftest.py new file mode 100644 index 00000000..ed285d51 --- /dev/null +++ b/app/access/tests/unit/contact/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from access.models.contact import Contact + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Contact + + yield request.cls.model + + del request.cls.model diff --git a/app/access/tests/unit/contact/test_unit_contact_api_fields.py b/app/access/tests/unit/contact/test_unit_contact_api_fields.py new file mode 100644 index 00000000..5558f4e2 --- /dev/null +++ b/app/access/tests/unit/contact/test_unit_contact_api_fields.py @@ -0,0 +1,40 @@ +from access.tests.unit.person.test_unit_person_api_fields import ( + PersonAPIInheritedCases +) + + + +class ContactAPITestCases( + PersonAPIInheritedCases, +): + + parameterized_test_data = { + 'email': { + 'expected': str + }, + 'directory': { + 'expected': bool + } + } + + kwargs_create_item: dict = { + 'email': 'ipfunny@unit.test', + } + + + +class ContactAPIInheritedCases( + ContactAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + + +class ContactAPIPyTest( + ContactAPITestCases, +): + + pass diff --git a/app/access/tests/unit/contact/test_unit_contact_api_v2.py b/app/access/tests/unit/contact/test_unit_contact_api_v2.py deleted file mode 100644 index 18a706c4..00000000 --- a/app/access/tests/unit/contact/test_unit_contact_api_v2.py +++ /dev/null @@ -1,90 +0,0 @@ -from django.test import TestCase - -from access.models.contact import Contact - -from access.tests.unit.person.test_unit_person_api_v2 import ( - PersonAPIInheritedCases, -) - - - -class APITestCases( - PersonAPIInheritedCases, -): - - model = Contact - - kwargs_item_create: dict = { - 'email': 'ipfunny@unit.test', - } - - url_ns_name = '_api_v2_entity_sub' - - - def test_api_field_exists_email(self): - """ Test for existance of API Field - - email field must exist - """ - - assert 'email' in self.api_data - - - def test_api_field_type_email(self): - """ Test for type for API Field - - email field must be str - """ - - assert type(self.api_data['email']) is str - - - def test_api_field_exists_directory(self): - """ Test for existance of API Field - - directory field must exist - """ - - assert 'directory' in self.api_data - - - def test_api_field_type_directory(self): - """ Test for type for API Field - - directory field must be bool - """ - - assert type(self.api_data['directory']) is bool - - - -class ContactAPIInheritedCases( - APITestCases, -): - """Sub-Entity Test Cases - - Test Cases for Entity models that inherit from model Contact - """ - - kwargs_item_create: dict = None - - model = None - - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create.update( - super().kwargs_item_create - ) - - super().setUpTestData() - - - -class ContactAPITest( - APITestCases, - TestCase, -): - - pass From be02061b947c7621381574f8edac97f6da142185 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:03:02 +0930 Subject: [PATCH 144/175] refactor(access): Update Model Person to use PyTest API Fields Render ref: #761 #730 --- app/access/tests/unit/person/conftest.py | 14 ++ .../person/test_unit_person_api_fields.py | 50 +++++++ .../unit/person/test_unit_person_api_v2.py | 127 ------------------ 3 files changed, 64 insertions(+), 127 deletions(-) create mode 100644 app/access/tests/unit/person/conftest.py create mode 100644 app/access/tests/unit/person/test_unit_person_api_fields.py delete mode 100644 app/access/tests/unit/person/test_unit_person_api_v2.py diff --git a/app/access/tests/unit/person/conftest.py b/app/access/tests/unit/person/conftest.py new file mode 100644 index 00000000..f0a1063e --- /dev/null +++ b/app/access/tests/unit/person/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from access.models.person import Person + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Person + + yield request.cls.model + + del request.cls.model diff --git a/app/access/tests/unit/person/test_unit_person_api_fields.py b/app/access/tests/unit/person/test_unit_person_api_fields.py new file mode 100644 index 00000000..be1cbc2b --- /dev/null +++ b/app/access/tests/unit/person/test_unit_person_api_fields.py @@ -0,0 +1,50 @@ +from access.tests.unit.entity.test_unit_entity_api_fields import ( + EntityAPIInheritedCases +) + + + +class PersonAPITestCases( + EntityAPIInheritedCases, +): + + parameterized_test_data = { + 'f_name': { + 'expected': str + }, + 'm_name': { + 'expected': str + }, + 'l_name': { + 'expected': str + }, + 'dob': { + 'expected': str + } + } + + kwargs_create_item: dict = { + 'entity_type': 'person', + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Funny', + 'dob': '2025-04-08', + } + + + +class PersonAPIInheritedCases( + PersonAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + + +class PersonAPIPyTest( + PersonAPITestCases, +): + + pass diff --git a/app/access/tests/unit/person/test_unit_person_api_v2.py b/app/access/tests/unit/person/test_unit_person_api_v2.py deleted file mode 100644 index b652ba32..00000000 --- a/app/access/tests/unit/person/test_unit_person_api_v2.py +++ /dev/null @@ -1,127 +0,0 @@ -from django.test import TestCase - -from access.models.person import Person - -from access.tests.unit.entity.test_unit_entity_api_v2 import ( - EntityAPIInheritedCases, -) - - - -class APITestCases( - EntityAPIInheritedCases, -): - - model = Person - - kwargs_item_create: dict = {} - - url_ns_name = '_api_v2_entity_sub' - - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create.update({ - 'f_name': 'Ian', - 'm_name': 'Peter', - 'l_name': 'Funny', - 'dob': '2025-04-08', - }) - - super().setUpTestData() - - - - def test_api_field_exists_f_name(self): - """ Test for existance of API Field - - f_name field must exist - """ - - assert 'f_name' in self.api_data - - - def test_api_field_type_f_name(self): - """ Test for type for API Field - - f_name field must be str - """ - - assert type(self.api_data['f_name']) is str - - - def test_api_field_exists_m_name(self): - """ Test for existance of API Field - - m_name field must exist - """ - - assert 'm_name' in self.api_data - - - def test_api_field_type_f_name(self): - """ Test for type for API Field - - m_name field must be str - """ - - assert type(self.api_data['m_name']) is str - - - def test_api_field_exists_l_name(self): - """ Test for existance of API Field - - l_name field must exist - """ - - assert 'l_name' in self.api_data - - - def test_api_field_type_f_name(self): - """ Test for type for API Field - - l_name field must be str - """ - - assert type(self.api_data['l_name']) is str - - - def test_api_field_exists_dob(self): - """ Test for existance of API Field - - dob field must exist - """ - - assert 'dob' in self.api_data - - - def test_api_field_type_dob(self): - """ Test for type for API Field - - dob field must be str - """ - - assert type(self.api_data['dob']) is str - - - -class PersonAPIInheritedCases( - APITestCases, -): - """Sub-Entity Test Cases - - Test Cases for Entity models that inherit from model Person - """ - - kwargs_item_create: dict = None - - model = None - - -class PersonAPITest( - APITestCases, - TestCase, -): - - pass From e295e53f86713d3077a01d9ff86a913c25b591cc Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:03:19 +0930 Subject: [PATCH 145/175] refactor(human_resources): Update Model Employee to use PyTest API Fields Render ref: #761 #730 --- .../tests/unit/employee/conftest.py | 14 ++++ .../employee/test_unit_employee_api_fields.py | 37 ++++++++++ .../employee/test_unit_employee_api_v2.py | 73 ------------------- 3 files changed, 51 insertions(+), 73 deletions(-) create mode 100644 app/human_resources/tests/unit/employee/conftest.py create mode 100644 app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py delete mode 100644 app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py diff --git a/app/human_resources/tests/unit/employee/conftest.py b/app/human_resources/tests/unit/employee/conftest.py new file mode 100644 index 00000000..9300eb74 --- /dev/null +++ b/app/human_resources/tests/unit/employee/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from human_resources.models.employee import Employee + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Employee + + yield request.cls.model + + del request.cls.model diff --git a/app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py b/app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py new file mode 100644 index 00000000..2397edba --- /dev/null +++ b/app/human_resources/tests/unit/employee/test_unit_employee_api_fields.py @@ -0,0 +1,37 @@ +from access.tests.unit.contact.test_unit_contact_api_fields import ( + ContactAPIInheritedCases +) + + + +class EmployeeAPITestCases( + ContactAPIInheritedCases, +): + + parameterized_test_data = { + 'employee_number': { + 'expected': int + } + } + + kwargs_create_item: dict = { + 'employee_number': 12345, + } + + + +class EmployeeAPIInheritedCases( + EmployeeAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + + +class EmployeeAPIPyTest( + EmployeeAPITestCases, +): + + pass diff --git a/app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py b/app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py deleted file mode 100644 index b7db910c..00000000 --- a/app/human_resources/tests/unit/employee/test_unit_employee_api_v2.py +++ /dev/null @@ -1,73 +0,0 @@ -from django.test import TestCase - -from access.tests.unit.contact.test_unit_contact_api_v2 import ( - ContactAPIInheritedCases, -) - -from human_resources.models.employee import Employee - - - -class APITestCases( - ContactAPIInheritedCases, -): - - model = Employee - - kwargs_item_create: dict = { - 'email': 'ipfunny@unit.test', - 'employee_number': 123456, - } - - url_ns_name = '_api_v2_entity_sub' - - - def test_api_field_exists_employee_number(self): - """ Test for existance of API Field - - employee_number field must exist - """ - - assert 'employee_number' in self.api_data - - - def test_api_field_type_employee_number(self): - """ Test for type for API Field - - employee_number field must be str - """ - - assert type(self.api_data['employee_number']) is int - - - -class EmployeeAPIInheritedCases( - APITestCases, -): - """Sub-Entity Test Cases - - Test Cases for Entity models that inherit from model Employee - """ - - kwargs_item_create: dict = None - - model = None - - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create.update( - super().kwargs_item_create - ) - - super().setUpTestData() - - - -class EmployeeAPITest( - APITestCases, - TestCase, -): - - pass From 3c19e9d4cf02190159a9b480eac6b01697b48074 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 20:19:43 +0930 Subject: [PATCH 146/175] fix(api): Dont try to access attribute if not exist in common viewset ref: #761 --- app/api/viewsets/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index 4a5a2629..29c2435e 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -841,7 +841,7 @@ class SubModelViewSet( if( str( - related_model._meta.sub_model_type + getattr(related_model, '._meta.sub_model_type', '') ).lower().replace(' ', '_') == model_kwarg ): From c2a367bd282d7693d33ec71af6a9940399ddfada Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:15:43 +0930 Subject: [PATCH 147/175] refactor(human_resources): Update Model Employee to use PyTest for Model Test Suite ref: #761 #730 --- .../unit/employee/test_unit_employee_model.py | 102 +++++++++++------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/app/human_resources/tests/unit/employee/test_unit_employee_model.py b/app/human_resources/tests/unit/employee/test_unit_employee_model.py index 87f45428..045886f1 100644 --- a/app/human_resources/tests/unit/employee/test_unit_employee_model.py +++ b/app/human_resources/tests/unit/employee/test_unit_employee_model.py @@ -1,8 +1,8 @@ -from django.db.models.fields import NOT_PROVIDED -from django.test import TestCase +import pytest + +from django.db import models from access.tests.unit.contact.test_unit_contact_model import ( - Contact, ContactModelInheritedCases ) @@ -10,72 +10,96 @@ from human_resources.models.employee import Employee -class ModelTestCases( +class EmployeeModelTestCases( ContactModelInheritedCases, ): - model = Employee + kwargs_create_item: dict = { + 'employee_number': 12345, + } - kwargs_item_create: dict = { - 'email': 'ipweird@unit.test', - 'employee_number': 123456, + sub_model_type = 'employee' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + + parameterized_fields: dict = { + "employee_number": { + 'field_type': models.fields.IntegerField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + } } - def test_model_field_employee_number_mandatory(self): - """Test Field + def test_class_inherits_employee(self): + """ Class inheritence - Field `employee_number` must be a mandatory field + TenancyObject must inherit SaveHistory """ - assert( - not ( - self.model._meta.get_field('employee_number').blank - and self.model._meta.get_field('employee_number').null - ) - and self.model._meta.get_field('employee_number').default is NOT_PROVIDED - ) + assert issubclass(self.model, Employee) - def test_model_inherits_contact(self): - """Test model inheritence + def test_attribute_value_history_app_label(self): + """Attribute Type - model must inherit from Entity sub-model `Contact` + history_app_label has been set, override this test case with the value + of attribute `history_app_label` """ - assert issubclass(self.model, Contact) + assert self.model.history_app_label == 'human_resources' + def test_attribute_value_history_model_name(self): + """Attribute Type + + history_model_name has been set, override this test case with the value + of attribute `history_model_name` + """ + + assert self.model.history_model_name == 'employee' + + + + def test_function_value_get_url(self): + + assert self.item.get_url() == '/api/v2/access/entity/employee/' + str(self.item.id) + class EmployeeModelInheritedCases( - ModelTestCases, + EmployeeModelTestCases, ): - """Sub-Entity Test Cases + """Sub-Ticket Test Cases - Test Cases for Entity models that inherit from model Employee + Test Cases for Ticket models that inherit from model Entity """ - kwargs_item_create: dict = None + kwargs_create_item: dict = {} model = None - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create.update( - super().kwargs_item_create - ) - - super().setUpTestData() + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ -class EmployeeModelTest( - ModelTestCases, - TestCase, +class EmployeeModelPyTest( + EmployeeModelTestCases, ): - pass + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is None for base model + """ + + assert self.item.get_related_model() is None From cf7eeb5bde5512f4c552b22ecb90303302aedd7a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:16:45 +0930 Subject: [PATCH 148/175] refactor(access): Update Model Person to use PyTest for Model Test Suite ref: #761 #730 --- ...y_entity_type_alter_person_dob_and_more.py | 28 ++++ app/access/models/person.py | 2 - .../unit/person/test_unit_person_model.py | 121 +++++++++++------- 3 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py diff --git a/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py b/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py new file mode 100644 index 00000000..b9c25071 --- /dev/null +++ b/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.9 on 2025-05-15 12:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('access', '0010_company'), + ] + + operations = [ + migrations.AlterField( + model_name='entity', + name='entity_type', + field=models.CharField(help_text='Type this entity is', max_length=30, verbose_name='Entity Type'), + ), + migrations.AlterField( + model_name='person', + name='dob', + field=models.DateField(blank=True, help_text='The Persons Date of Birth (DOB)', null=True, verbose_name='DOB'), + ), + migrations.AlterField( + model_name='person', + name='m_name', + field=models.CharField(blank=True, help_text='The persons middle name(s)', max_length=100, null=True, verbose_name='Middle Name(s)'), + ), + ] diff --git a/app/access/models/person.py b/app/access/models/person.py index fe4ca11c..61c73335 100644 --- a/app/access/models/person.py +++ b/app/access/models/person.py @@ -36,7 +36,6 @@ class Person( m_name = models.CharField( blank = True, - default = None, help_text = 'The persons middle name(s)', max_length = 100, null = True, @@ -54,7 +53,6 @@ class Person( dob = models.DateField( blank = True, - default = None, help_text = 'The Persons Date of Birth (DOB)', null = True, unique = False, diff --git a/app/access/tests/unit/person/test_unit_person_model.py b/app/access/tests/unit/person/test_unit_person_model.py index 94a86229..ee3a1b46 100644 --- a/app/access/tests/unit/person/test_unit_person_model.py +++ b/app/access/tests/unit/person/test_unit_person_model.py @@ -1,5 +1,6 @@ -from django.db.models.fields import NOT_PROVIDED -from django.test import TestCase +import pytest + +from django.db import models from access.models.person import Person from access.tests.unit.entity.test_unit_entity_model import ( @@ -8,88 +9,114 @@ from access.tests.unit.entity.test_unit_entity_model import ( -class ModelTestCases( +class PersonModelTestCases( EntityModelInheritedCases, ): - model = Person - - kwargs_item_create: dict = { + kwargs_create_item: dict = { 'f_name': 'Ian', 'm_name': 'Peter', 'l_name': 'Funny', 'dob': '2025-04-08', } + sub_model_type = 'person' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ - def test_model_field_dob_optional(self): - """Test Field + parameterized_fields: dict = { + "f_name": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "m_name": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "l_name": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "dob": { + 'field_type': models.fields.DateField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + } - Field `dob` must be an optional field + + + def test_class_inherits_person(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory """ - assert self.model._meta.get_field('dob').blank + assert issubclass(self.model, Person) - def test_model_field_f_name_mandatory(self): - """Test Field + def test_attribute_value_history_app_label(self): + """Attribute Type - Field `f_name` must be a mandatory field + history_app_label has been set, override this test case with the value + of attribute `history_app_label` """ - assert( - not ( - self.model._meta.get_field('f_name').blank - and self.model._meta.get_field('f_name').null - ) - and self.model._meta.get_field('f_name').default is NOT_PROVIDED - ) + assert self.model.history_app_label == 'access' - def test_model_field_l_name_mandatory(self): - """Test Field + def test_attribute_value_history_model_name(self): + """Attribute Type - Field `l_name` must be a mandatory field + history_model_name has been set, override this test case with the value + of attribute `history_model_name` """ - assert ( - not ( - self.model._meta.get_field('l_name').blank - and self.model._meta.get_field('l_name').null - ) - and self.model._meta.get_field('l_name').default is NOT_PROVIDED - ) + assert self.model.history_model_name == 'person' + + + + def test_function_value_get_url(self): + + assert self.item.get_url() == '/api/v2/access/entity/person/' + str(self.item.id) class PersonModelInheritedCases( - ModelTestCases, + PersonModelTestCases, ): - """Sub-Entity Test Cases + """Sub-Ticket Test Cases - Test Cases for Entity models that inherit from model Person + Test Cases for Ticket models that inherit from model Entity """ - kwargs_item_create: dict = None + kwargs_create_item: dict = {} model = None - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create.update( - super().kwargs_item_create - ) - - super().setUpTestData() + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ -class PersonModelTest( - ModelTestCases, - TestCase, +class PersonModelPyTest( + PersonModelTestCases, ): - pass + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is None for base model + """ + + assert self.item.get_related_model() is None From 1150e1b04797bddcf97e3da707c739272511915a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:16:56 +0930 Subject: [PATCH 149/175] refactor(access): Update Model Contact to use PyTest for Model Test Suite ref: #761 #730 --- .../unit/contact/test_unit_contact_model.py | 122 ++++++++++-------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/app/access/tests/unit/contact/test_unit_contact_model.py b/app/access/tests/unit/contact/test_unit_contact_model.py index 10335e32..cd7509c8 100644 --- a/app/access/tests/unit/contact/test_unit_contact_model.py +++ b/app/access/tests/unit/contact/test_unit_contact_model.py @@ -1,100 +1,110 @@ -from django.db.models.fields import NOT_PROVIDED -from django.test import TestCase +import pytest + +from django.db import models from access.models.contact import Contact from access.tests.unit.person.test_unit_person_model import ( - Person, PersonModelInheritedCases ) -class ModelTestCases( +class ContactModelTestCases( PersonModelInheritedCases, ): - model = Contact - - kwargs_item_create: dict = { + kwargs_create_item: dict = { 'email': 'ipweird@unit.test', } + sub_model_type = 'contact' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + + parameterized_fields: dict = { + "email": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + }, + "directory": { + 'field_type': models.fields.BooleanField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': True, + 'field_parameter_verbose_name_type': str, + } + } + - def test_model_field_directory_optional(self): - """Test Field + def test_class_inherits_contact(self): + """ Class inheritence - Field `dob` must be an optional field + TenancyObject must inherit SaveHistory """ - assert self.model._meta.get_field('directory').blank + assert issubclass(self.model, Contact) - def test_model_field_directory_optional_default(self): - """Test Field + # def test_attribute_value_history_app_label(self): + # """Attribute Type - Field `directory` default value is `True` + # history_app_label has been set, override this test case with the value + # of attribute `history_app_label` + # """ + + # assert self.model.history_app_label == 'access' + + + def test_attribute_value_history_model_name(self): + """Attribute Type + + history_model_name has been set, override this test case with the value + of attribute `history_model_name` """ - assert ( - self.model._meta.get_field('directory').default is True - and self.model._meta.get_field('directory').null is False - ) + assert self.model.history_model_name == 'contact' - def test_model_field_email_mandatory(self): - """Test Field - Field `email` must be a mandatory field - """ - - assert( - not ( - self.model._meta.get_field('email').blank - and self.model._meta.get_field('email').null - ) - and self.model._meta.get_field('email').default is NOT_PROVIDED - ) - - - def test_model_inherits_person(self): - """Test model inheritence - - model must inherit from Entity sub-model `Person` - """ - - assert issubclass(self.model, Person) + def test_function_value_get_url(self): + assert self.item.get_url() == '/api/v2/access/entity/contact/' + str(self.item.id) class ContactModelInheritedCases( - ModelTestCases, + ContactModelTestCases, ): - """Sub-Entity Test Cases + """Sub-Ticket Test Cases - Test Cases for Entity models that inherit from model Contact + Test Cases for Ticket models that inherit from model Entity """ - kwargs_item_create: dict = None + kwargs_create_item: dict = {} model = None - - @classmethod - def setUpTestData(self): - - self.kwargs_item_create.update( - super().kwargs_item_create - ) - - super().setUpTestData() + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ -class ContactModelTest( - ModelTestCases, - TestCase, +class ContactModelPyTest( + ContactModelTestCases, ): - pass + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is None for base model + """ + + assert self.item.get_related_model() is None From d29df73d05c5c9b22f7ba485dbcae93f24663a01 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:17:05 +0930 Subject: [PATCH 150/175] refactor(access): Update Model Entity to use PyTest for Model Test Suite ref: #761 #730 --- app/access/models/entity.py | 2 +- .../unit/entity/test_unit_entity_model.py | 202 +++++++++++++++--- app/app/tests/unit/test_unit_models.py | 36 ++-- 3 files changed, 197 insertions(+), 43 deletions(-) diff --git a/app/access/models/entity.py b/app/access/models/entity.py index 29b4584e..ae602311 100644 --- a/app/access/models/entity.py +++ b/app/access/models/entity.py @@ -37,9 +37,9 @@ class Entity( verbose_name = 'ID' ) + entity_type = models.CharField( blank = False, - default = Meta.verbose_name.lower(), help_text = 'Type this entity is', max_length = 30, unique = False, diff --git a/app/access/tests/unit/entity/test_unit_entity_model.py b/app/access/tests/unit/entity/test_unit_entity_model.py index bb29c6ed..acf45745 100644 --- a/app/access/tests/unit/entity/test_unit_entity_model.py +++ b/app/access/tests/unit/entity/test_unit_entity_model.py @@ -1,27 +1,120 @@ -from django.test import TestCase +import pytest + +from django.db import models from access.models.entity import Entity from app.tests.unit.test_unit_models import ( - TenancyObjectInheritedCases + PyTestTenancyObjectInheritedCases, ) - class EntityModelTestCases( - TenancyObjectInheritedCases, + PyTestTenancyObjectInheritedCases, ): - model = Entity + base_model = Entity - kwargs_item_create: dict = {} + kwargs_create_item: dict = {} + + sub_model_type = 'entity' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + + parameterized_fields: dict = { + "entity_type": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + # 'field_parameter_default_value': 'entity', + 'field_parameter_verbose_name_type': str + }, + # "asset_number": { + # 'field_type': models.fields.CharField, + # 'field_parameter_default_exists': False, + # 'field_parameter_verbose_name_type': str, + # }, + # "serial_number": { + # 'field_type': models.fields.CharField, + # 'field_parameter_default_exists': False, + # 'field_parameter_verbose_name_type': str, + # } + } + + + + @pytest.fixture( scope = 'class') + def setup_model(self, + request, + model, + django_db_blocker, + organization_one, + organization_two + ): + + with django_db_blocker.unblock(): + + request.cls.organization = organization_one + + request.cls.different_organization = organization_two + + kwargs_create_item = {} + + for base in reversed(request.cls.__mro__): + + if hasattr(base, 'kwargs_create_item'): + + if base.kwargs_create_item is None: + + continue + + kwargs_create_item.update(**base.kwargs_create_item) + + + if len(kwargs_create_item) > 0: + + request.cls.kwargs_create_item = kwargs_create_item + + + if 'organization' not in request.cls.kwargs_create_item: + + request.cls.kwargs_create_item.update({ + 'organization': request.cls.organization + }) + + yield + + with django_db_blocker.unblock(): + + del request.cls.kwargs_create_item + + + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_model, + create_model, + ): + + pass + + + + def test_class_inherits_entity(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory + """ + + assert issubclass(self.model, Entity) def test_attribute_type_history_app_label(self): """Attribute Type - history_app_name is of type str + history_app_label is of type str """ assert type(self.model.history_app_label) is str @@ -30,11 +123,11 @@ class EntityModelTestCases( def test_attribute_value_history_app_label(self): """Attribute Type - history_app_name is of type str + history_app_label has been set, override this test case with the value + of attribute `history_app_label` """ - assert self.model.history_app_label == self.model._meta.app_label - + assert self.model.history_app_label == 'access' @@ -50,10 +143,11 @@ class EntityModelTestCases( def test_attribute_value_history_model_name(self): """Attribute Type - history_model_name is of type str + history_model_name has been set, override this test case with the value + of attribute `history_model_name` """ - assert self.model.history_model_name == self.model._meta.model_name + assert self.model.history_model_name == 'entity' @@ -69,7 +163,8 @@ class EntityModelTestCases( def test_attribute_value_kb_model_name(self): """Attribute Type - kb_model_name is of type str + kb_model_name has been set, override this test case with the value + of attribute `kb_model_name` """ assert self.model.kb_model_name == 'entity' @@ -88,40 +183,95 @@ class EntityModelTestCases( def test_attribute_value_note_basename(self): """Attribute Type - note_basename is of type str + note_basename has been set, override this test case with the value + of attribute `note_basename` """ assert self.model.note_basename == '_api_v2_entity_note' + + + + + + # def test_function_is_property_get_model_type(self): + # """Function test + + # Confirm function `get_model_type` is a property + # """ + + # assert type(self.model.get_model_type) is property + + + # def test_function_value_get_model_type(self): + # """Function test + + # Confirm function `get_model_type` does not have a value of None + # value should be equaul to Meta.sub_model_type + # """ + + # assert self.item.get_model_type == self.item._meta.sub_model_type + + + + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is of the sub-model type + """ + + assert type(self.item.get_related_model()) == self.model + + + def test_function_value_get_url(self): + + assert self.item.get_url() == '/api/v2/access/entity/' + str(self.item.id) + + + class EntityModelInheritedCases( EntityModelTestCases, ): - """Sub-Entity Test Cases + """Sub-Ticket Test Cases - Test Cases for Entity models that inherit from model Entity + Test Cases for Ticket models that inherit from model Entity """ - kwargs_item_create: dict = None + kwargs_create_item: dict = {} model = None - @classmethod - def setUpTestData(self): + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ - self.kwargs_item_create.update( - super().kwargs_item_create - ) - super().setUpTestData() + # def test_function_value_get_model_type(self): + # """Function test + + # Confirm function `get_model_type` does not have a value of None + # value should be equaul to Meta.sub_model_type + # """ + + # assert self.item.get_model_type == self.item._meta.sub_model_type -class EntityModelTest( +class EntityModelPyTest( EntityModelTestCases, - TestCase, ): - pass + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is None for base model + """ + + assert self.item.get_related_model() is None diff --git a/app/app/tests/unit/test_unit_models.py b/app/app/tests/unit/test_unit_models.py index 8233438a..1fb76de8 100644 --- a/app/app/tests/unit/test_unit_models.py +++ b/app/app/tests/unit/test_unit_models.py @@ -402,23 +402,27 @@ class NonTenancyObjectInheritedCases( class ModelFieldsTestCasesReWrite: - parameterized_fields: dict = { - "organization": { - 'field_type': fields.Field, - 'field_parameter_default_exists': False, - 'field_parameter_verbose_name_type': str - }, - "model_notes": { - 'field_type': fields.TextField, - 'field_parameter_verbose_name_type': str - }, - "is_global": { - 'field_type': fields.BooleanField, - 'field_parameter_default_exists': True, - 'field_parameter_default_value': False, - 'field_parameter_verbose_name_type': str + + @property + def parameterized_fields(self) -> dict: + + return { + "organization": { + 'field_type': fields.Field, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str + }, + "model_notes": { + 'field_type': fields.TextField, + 'field_parameter_verbose_name_type': str + }, + "is_global": { + 'field_type': fields.BooleanField, + 'field_parameter_default_exists': True, + 'field_parameter_default_value': False, + 'field_parameter_verbose_name_type': str + } } - } From 2a977bbf471548380d82edfec34f497ebbf253b6 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:18:29 +0930 Subject: [PATCH 151/175] test(access): Model Test Suite Company model ref: #761 #760 --- app/access/tests/unit/company/conftest.py | 14 +++ .../unit/company/test_unit_company_model.py | 105 ++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 app/access/tests/unit/company/conftest.py create mode 100644 app/access/tests/unit/company/test_unit_company_model.py diff --git a/app/access/tests/unit/company/conftest.py b/app/access/tests/unit/company/conftest.py new file mode 100644 index 00000000..78945022 --- /dev/null +++ b/app/access/tests/unit/company/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from access.models.company_base import Company + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Company + + yield request.cls.model + + del request.cls.model diff --git a/app/access/tests/unit/company/test_unit_company_model.py b/app/access/tests/unit/company/test_unit_company_model.py new file mode 100644 index 00000000..c47efa7f --- /dev/null +++ b/app/access/tests/unit/company/test_unit_company_model.py @@ -0,0 +1,105 @@ +import pytest + +from django.db import models + +from access.models.company_base import Company + +from access.tests.unit.entity.test_unit_entity_model import ( + EntityModelInheritedCases +) + + + +class CompanyModelTestCases( + EntityModelInheritedCases, +): + + kwargs_create_item: dict = { + 'name': 'Ian', + } + + sub_model_type = 'company' + """Sub Model Type + + sub-models must have this attribute defined in `ModelName.Meta.sub_model_type` + """ + + + parameterized_fields: dict = { + "name": { + 'field_type': models.fields.CharField, + 'field_parameter_default_exists': False, + 'field_parameter_verbose_name_type': str, + } + } + + + + def test_class_inherits_company(self): + """ Class inheritence + + TenancyObject must inherit SaveHistory + """ + + assert issubclass(self.model, Company) + + + def test_attribute_value_history_app_label(self): + """Attribute Type + + history_app_label has been set, override this test case with the value + of attribute `history_app_label` + """ + + assert self.model.history_app_label == 'access' + + + def test_attribute_value_history_model_name(self): + """Attribute Type + + history_model_name has been set, override this test case with the value + of attribute `history_model_name` + """ + + assert self.model.history_model_name == 'company' + + + + def test_function_value_get_url(self): + + assert self.item.get_url() == '/api/v2/access/entity/company/' + str(self.item.id) + + + +class CompanyModelInheritedCases( + CompanyModelTestCases, +): + """Sub-Ticket Test Cases + + Test Cases for Ticket models that inherit from model Entity + """ + + kwargs_create_item: dict = {} + + model = None + + sub_model_type = None + """Ticket Sub Model Type + + Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type` + """ + + + +class CompanyModelPyTest( + CompanyModelTestCases, +): + + + def test_function_value_get_related_model(self): + """Function test + + Confirm function `get_related_model` is None for base model + """ + + assert self.item.get_related_model() is None From 900e03791a33b44274d8d4682abc2261ba3efd8a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:18:45 +0930 Subject: [PATCH 152/175] test(access): API field render Test Suite Company model ref: #761 #760 --- .../company/test_unit_company_api_fields.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 app/access/tests/unit/company/test_unit_company_api_fields.py diff --git a/app/access/tests/unit/company/test_unit_company_api_fields.py b/app/access/tests/unit/company/test_unit_company_api_fields.py new file mode 100644 index 00000000..2fe835ec --- /dev/null +++ b/app/access/tests/unit/company/test_unit_company_api_fields.py @@ -0,0 +1,37 @@ +from access.tests.unit.entity.test_unit_entity_api_fields import ( + EntityAPIInheritedCases +) + + + +class CompanyAPITestCases( + EntityAPIInheritedCases, +): + + parameterized_test_data = { + 'name': { + 'expected': str + } + } + + kwargs_create_item: dict = { + 'name': 'Ian' + } + + + +class CompanyAPIInheritedCases( + CompanyAPITestCases, +): + + kwargs_create_item: dict = None + + model = None + + + +class CompanyAPIPyTest( + CompanyAPITestCases, +): + + pass From edc7aedbe03c59007358a3e5e48018e201db5c5c Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 22:34:12 +0930 Subject: [PATCH 153/175] fix(api): Dont try to access attribute if not exist in common viewset ref: #761 --- .../tests/unit/entity/test_unit_entity_model.py | 3 --- app/api/viewsets/common.py | 12 +++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/access/tests/unit/entity/test_unit_entity_model.py b/app/access/tests/unit/entity/test_unit_entity_model.py index acf45745..fb7290bf 100644 --- a/app/access/tests/unit/entity/test_unit_entity_model.py +++ b/app/access/tests/unit/entity/test_unit_entity_model.py @@ -87,9 +87,6 @@ class EntityModelTestCases( yield - with django_db_blocker.unblock(): - - del request.cls.kwargs_create_item @pytest.fixture( scope = 'class', autouse = True) diff --git a/app/api/viewsets/common.py b/app/api/viewsets/common.py index 29c2435e..03edde97 100644 --- a/app/api/viewsets/common.py +++ b/app/api/viewsets/common.py @@ -839,17 +839,19 @@ class SubModelViewSet( is_nested_lookup = True - if( + + if not hasattr(related_model, '_meta'): + + related_model = None + + elif( str( - getattr(related_model, '._meta.sub_model_type', '') + getattr(related_model._meta, 'sub_model_type', '') ).lower().replace(' ', '_') == model_kwarg ): break - else: - - related_model = None if related_model is None and not is_nested_lookup: From bdeae5b38b5323da07066514717ddf5408a95f8a Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 15 May 2025 23:07:08 +0930 Subject: [PATCH 154/175] test(access): ViewSet Test Suite Company model ref: #761 #760 --- .../unit/company/test_unit_company_viewset.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 app/access/tests/unit/company/test_unit_company_viewset.py diff --git a/app/access/tests/unit/company/test_unit_company_viewset.py b/app/access/tests/unit/company/test_unit_company_viewset.py new file mode 100644 index 00000000..97bc62b9 --- /dev/null +++ b/app/access/tests/unit/company/test_unit_company_viewset.py @@ -0,0 +1,36 @@ +from django.test import TestCase + +from access.models.company_base import Company +from access.tests.unit.entity.test_unit_entity_viewset import ( + EntityViewsetInheritedCases +) + + + +class ViewsetTestCases( + EntityViewsetInheritedCases, +): + + model: str = Company + + + +class CompanyViewsetInheritedCases( + ViewsetTestCases, +): + """Sub-Entity Test Cases + + Test Cases for Entity models that inherit from model Company + """ + + model: str = None + """name of the model to test""" + + + +class CompanyViewsetTest( + ViewsetTestCases, + TestCase, +): + + pass From 85986ce13e14892bc8423eb66a2d3b6849fb549a Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:14:23 +0930 Subject: [PATCH 155/175] test(access): Functional MetaData Test Suite Company model ref: #761 #760 --- .../tests/functional/company/conftest.py | 24 +++++++ .../test_functional_company_metadata.py | 72 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 app/access/tests/functional/company/conftest.py create mode 100644 app/access/tests/functional/company/test_functional_company_metadata.py diff --git a/app/access/tests/functional/company/conftest.py b/app/access/tests/functional/company/conftest.py new file mode 100644 index 00000000..f9822817 --- /dev/null +++ b/app/access/tests/functional/company/conftest.py @@ -0,0 +1,24 @@ +import pytest + +from access.models.company_base import Company + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Company + + yield request.cls.model + + del request.cls.model + + + +@pytest.fixture(scope='function') +def create_serializer(): + + from access.serializers.entity_company import ModelSerializer + + + yield ModelSerializer diff --git a/app/access/tests/functional/company/test_functional_company_metadata.py b/app/access/tests/functional/company/test_functional_company_metadata.py new file mode 100644 index 00000000..c882ac34 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_metadata.py @@ -0,0 +1,72 @@ +from django.test import TestCase + +from access.models.company_base import Company +from access.tests.functional.entity.test_functional_entity_metadata import ( + EntityMetadataInheritedCases +) + + + +class CompanyMetadataTestCases( + EntityMetadataInheritedCases, +): + + add_data: dict = { + 'name': 'Ian1' + } + + kwargs_create_item: dict = { + 'name': 'Ian2', + } + + kwargs_create_item_diff_org: dict = { + 'name': 'Ian3', + } + + model = Company + + + + +class CompanyMetadataInheritedCases( + CompanyMetadataTestCases, +): + + model = None + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + + @classmethod + def setUpTestData(self): + + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + + # self.url_kwargs = { + # 'entity_model': self.model._meta.sub_model_type + # } + + # self.url_view_kwargs = { + # 'entity_model': self.model._meta.sub_model_type + # } + + super().setUpTestData() + + + +class CompanyMetadataTest( + CompanyMetadataTestCases, + TestCase, + +): + pass \ No newline at end of file From 46494c033c704a8609e2d1f5fcb24363f82af851 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:14:34 +0930 Subject: [PATCH 156/175] test(access): Functional Permissions Test Suite Company model ref: #761 #760 --- .../test_functional_company_permission.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/access/tests/functional/company/test_functional_company_permission.py diff --git a/app/access/tests/functional/company/test_functional_company_permission.py b/app/access/tests/functional/company/test_functional_company_permission.py new file mode 100644 index 00000000..df8239a8 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_permission.py @@ -0,0 +1,43 @@ +import pytest + +from access.tests.functional.entity.test_functional_entity_permission import ( + EntityPermissionsAPIInheritedCases +) + + + +class CompanyPermissionsAPITestCases( + EntityPermissionsAPIInheritedCases, +): + + add_data: dict = { + 'name': 'Ian1', + } + + kwargs_create_item: dict = { + 'name': 'Ian2', + } + + kwargs_create_item_diff_org: dict = { + 'name': 'Ian3', + } + + + +class CompanyPermissionsAPIInheritedCases( + CompanyPermissionsAPITestCases, +): + + add_data: dict = None + + kwargs_create_item: dict = None + + kwargs_create_item_diff_org: dict = None + + + +class CompanyPermissionsAPIPyTest( + CompanyPermissionsAPITestCases, +): + + pass From 99c0e923369851c607482f56bc15d60ffa4d68ac Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:14:50 +0930 Subject: [PATCH 157/175] test(access): Functional Serializer Test Suite Company model ref: #761 #760 --- .../test_functional_company_serializer.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 app/access/tests/functional/company/test_functional_company_serializer.py diff --git a/app/access/tests/functional/company/test_functional_company_serializer.py b/app/access/tests/functional/company/test_functional_company_serializer.py new file mode 100644 index 00000000..bafa2650 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_serializer.py @@ -0,0 +1,46 @@ +import pytest + +from rest_framework.exceptions import ValidationError + +from access.tests.functional.entity.test_functional_entity_serializer import ( + MockView, + EntitySerializerInheritedCases +) + + + +class CompanySerializerTestCases( + EntitySerializerInheritedCases +): + + + parameterized_test_data: dict = { + "name": { + 'will_create': False, + 'exception_key': 'required' + }, + } + + valid_data: dict = { + 'name': 'Ian', + } + """Valid data used by serializer to create object""" + + + +class CompanySerializerInheritedCases( + CompanySerializerTestCases, +): + + parameterized_test_data: dict = None + + valid_data: dict = None + """Valid data used by serializer to create object""" + + + +class CompanySerializerPyTest( + CompanySerializerTestCases, +): + + parameterized_test_data: dict = None From 7fee33999fad625181f0acbbd184a33482bf5dfa Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:15:56 +0930 Subject: [PATCH 158/175] refactor(access): Update Functional Metadata to use PyTest for Contact Model ref: #761 #730 --- .../tests/functional/contact/conftest.py | 24 +++++++ .../test_functional_contact_metadata.py | 65 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 app/access/tests/functional/contact/conftest.py create mode 100644 app/access/tests/functional/contact/test_functional_contact_metadata.py diff --git a/app/access/tests/functional/contact/conftest.py b/app/access/tests/functional/contact/conftest.py new file mode 100644 index 00000000..81e40636 --- /dev/null +++ b/app/access/tests/functional/contact/conftest.py @@ -0,0 +1,24 @@ +import pytest + +from access.models.contact import Contact + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Contact + + yield request.cls.model + + del request.cls.model + + + +@pytest.fixture(scope='function') +def create_serializer(): + + from access.serializers.entity_contact import ModelSerializer + + + yield ModelSerializer diff --git a/app/access/tests/functional/contact/test_functional_contact_metadata.py b/app/access/tests/functional/contact/test_functional_contact_metadata.py new file mode 100644 index 00000000..76125ef4 --- /dev/null +++ b/app/access/tests/functional/contact/test_functional_contact_metadata.py @@ -0,0 +1,65 @@ +from django.test import TestCase + +from access.models.contact import Contact + +from access.tests.functional.person.test_functional_person_metadata import ( + PersonMetadataInheritedCases +) + + + +class ContactMetadataTestCases( + PersonMetadataInheritedCases, +): + + add_data: dict = { + 'email': 'ipfunny@unit.test', + } + + kwargs_create_item: dict = { + 'email': 'ipweird@unit.test', + } + + kwargs_create_item_diff_org: dict = { + 'email': 'ipstrange@unit.test', + } + + model = Contact + + + + +class ContactMetadataInheritedCases( + ContactMetadataTestCases, +): + + model = None + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + + @classmethod + def setUpTestData(self): + + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + + super().setUpTestData() + + + +class ContactMetadataTest( + ContactMetadataTestCases, + TestCase, + +): + pass \ No newline at end of file From eb97bfbeeb63047ba753d8b02579383621c2ebaf Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:09 +0930 Subject: [PATCH 159/175] refactor(access): Update Functional Metadata to use PyTest for Entity Model ref: #761 #730 --- .../tests/functional/entity/conftest.py | 24 ++ .../entity/test_functional_entity_metadata.py | 260 ++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 app/access/tests/functional/entity/conftest.py create mode 100644 app/access/tests/functional/entity/test_functional_entity_metadata.py diff --git a/app/access/tests/functional/entity/conftest.py b/app/access/tests/functional/entity/conftest.py new file mode 100644 index 00000000..1c393761 --- /dev/null +++ b/app/access/tests/functional/entity/conftest.py @@ -0,0 +1,24 @@ +import pytest + +from access.models.entity import Entity + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Entity + + yield request.cls.model + + del request.cls.model + + + +@pytest.fixture(scope='function') +def create_serializer(): + + from access.serializers.entity import ModelSerializer + + + yield ModelSerializer diff --git a/app/access/tests/functional/entity/test_functional_entity_metadata.py b/app/access/tests/functional/entity/test_functional_entity_metadata.py new file mode 100644 index 00000000..55f0d01d --- /dev/null +++ b/app/access/tests/functional/entity/test_functional_entity_metadata.py @@ -0,0 +1,260 @@ +import django +from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType +from django.test import TestCase + +from access.models.entity import Entity +from access.models.tenant import Tenant as Organization +from access.models.team import Team +from access.models.team_user import TeamUsers + +from accounting.models.asset_base import AssetBase + +from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional + +User = django.contrib.auth.get_user_model() + + + +class EntityMetadataTestCases( + MetadataAttributesFunctional, +): + + add_data: dict = {} + + app_namespace = 'v2' + + base_model = Entity + """Base model for this sub model + don't change or override this value + """ + + change_data = None + + delete_data = {} + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + model = Entity + + url_kwargs: dict = {} + + url_view_kwargs: dict = {} + + url_name = None + + + @classmethod + def setUpTestData(self): + """Setup Test + + 1. Create an organization for user and item + . create an organization that is different to item + 2. Create a team + 3. create teams with each permission: view, add, change, delete + 4. create a user per team + """ + + organization = Organization.objects.create(name='test_org') + + self.organization = organization + + self.different_organization = Organization.objects.create(name='test_different_organization') + + self.view_user = User.objects.create_user(username="test_user_view", password="password") + + self.item = self.model.objects.create( + organization = organization, + **self.kwargs_create_item + ) + + self.other_org_item = self.model.objects.create( + organization = self.different_organization, + **self.kwargs_create_item_diff_org + ) + + + self.url_view_kwargs.update({ 'pk': self.item.id }) + + if self.add_data is not None: + + self.add_data.update({ + 'organization': self.organization.id, + }) + + + view_permissions = Permission.objects.get( + codename = 'view_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + view_team = Team.objects.create( + team_name = 'view_team', + organization = organization, + ) + + view_team.permissions.set([view_permissions]) + + + + add_permissions = Permission.objects.get( + codename = 'add_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + add_team = Team.objects.create( + team_name = 'add_team', + organization = organization, + ) + + add_team.permissions.set([add_permissions]) + + + + change_permissions = Permission.objects.get( + codename = 'change_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + change_team = Team.objects.create( + team_name = 'change_team', + organization = organization, + ) + + change_team.permissions.set([change_permissions]) + + + + delete_permissions = Permission.objects.get( + codename = 'delete_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + delete_team = Team.objects.create( + team_name = 'delete_team', + organization = organization, + ) + + delete_team.permissions.set([delete_permissions]) + + + self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password") + + + TeamUsers.objects.create( + team = view_team, + user = self.view_user + ) + + self.add_user = User.objects.create_user(username="test_user_add", password="password") + TeamUsers.objects.create( + team = add_team, + user = self.add_user + ) + + self.change_user = User.objects.create_user(username="test_user_change", password="password") + TeamUsers.objects.create( + team = change_team, + user = self.change_user + ) + + self.delete_user = User.objects.create_user(username="test_user_delete", password="password") + TeamUsers.objects.create( + team = delete_team, + user = self.delete_user + ) + + + self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password") + + + different_organization_team = Team.objects.create( + team_name = 'different_organization_team', + organization = self.different_organization, + ) + + different_organization_team.permissions.set([ + view_permissions, + add_permissions, + change_permissions, + delete_permissions, + ]) + + TeamUsers.objects.create( + team = different_organization_team, + user = self.different_organization_user + ) + + + def test_sanity_is_entity_sub_model(self): + """Sanity Test + + This test ensures that the model being tested `self.model` is a + sub-model of `self.base_model`. + This test is required as the same viewset is used for all sub-models + of `AssetBase` + """ + + assert issubclass(self.model, self.base_model) + + + +class EntityMetadataInheritedCases( + EntityMetadataTestCases, +): + + model = None + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + url_name = '_api_v2_entity_sub' + + + @classmethod + def setUpTestData(self): + + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + + self.url_kwargs = { + 'entity_model': self.model._meta.sub_model_type + } + + self.url_view_kwargs = { + 'entity_model': self.model._meta.sub_model_type + } + + super().setUpTestData() + + + +class EntityMetadataTest( + EntityMetadataTestCases, + TestCase, + +): + + url_name = '_api_v2_entity' From d0c35377531fc546785d1bc9cda7a2394238f2eb Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:19 +0930 Subject: [PATCH 160/175] refactor(access): Update Functional Metadata to use PyTest for Person Model ref: #761 #730 --- .../person/test_functional_person_metadata.py | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 app/access/tests/functional/person/test_functional_person_metadata.py diff --git a/app/access/tests/functional/person/test_functional_person_metadata.py b/app/access/tests/functional/person/test_functional_person_metadata.py new file mode 100644 index 00000000..f59f57bf --- /dev/null +++ b/app/access/tests/functional/person/test_functional_person_metadata.py @@ -0,0 +1,83 @@ +from django.test import TestCase + +from access.models.person import Person +from access.tests.functional.entity.test_functional_entity_metadata import ( + EntityMetadataInheritedCases +) + +from accounting.models.asset_base import AssetBase + + + +class PersonMetadataTestCases( + EntityMetadataInheritedCases, +): + + add_data: dict = { + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Strange', + 'dob': '2025-04-08', + } + + kwargs_create_item: dict = { + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Weird', + 'dob': '2025-04-08', + } + + kwargs_create_item_diff_org: dict = { + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Funny', + 'dob': '2025-04-08', + } + + model = Person + + + + +class PersonMetadataInheritedCases( + PersonMetadataTestCases, +): + + model = None + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + + @classmethod + def setUpTestData(self): + + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + + # self.url_kwargs = { + # 'entity_model': self.model._meta.sub_model_type + # } + + # self.url_view_kwargs = { + # 'entity_model': self.model._meta.sub_model_type + # } + + super().setUpTestData() + + + +class PersonMetadataTest( + PersonMetadataTestCases, + TestCase, + +): + pass \ No newline at end of file From 387ffc9adedc92fdfff68de0dbd3a80a049c6360 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:38 +0930 Subject: [PATCH 161/175] refactor(human_resources): Update Functional Metadata to use PyTest for Employee Model ref: #761 #730 --- .../test_functional_employee_metadata.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 app/human_resources/tests/functional/employee/test_functional_employee_metadata.py diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_metadata.py b/app/human_resources/tests/functional/employee/test_functional_employee_metadata.py new file mode 100644 index 00000000..df058e46 --- /dev/null +++ b/app/human_resources/tests/functional/employee/test_functional_employee_metadata.py @@ -0,0 +1,48 @@ +from django.test import TestCase + +from access.tests.functional.contact.test_functional_contact_metadata import ( + ContactMetadataInheritedCases +) + +from human_resources.models.employee import Employee + + + +class EmployeeMetadataTestCases( + ContactMetadataInheritedCases, +): + + add_data: dict = { + 'employee_number': 123456, + } + + kwargs_create_item: dict = { + 'employee_number': 1234568, + } + + kwargs_create_item_diff_org: dict = { + 'employee_number': 1234567, + } + + model = Employee + + + +class EmployeeMetadataInheritedCases( + EmployeeMetadataTestCases, +): + + model = None + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + + +class EmployeeMetadataTest( + EmployeeMetadataTestCases, + TestCase, + +): + pass \ No newline at end of file From 7a1aecebd5b2c0c111cf7e3e03aaeba2dba9db30 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:49 +0930 Subject: [PATCH 162/175] refactor(human_resources): Update Functional Permissions to use PyTest for Employee Model ref: #761 #730 --- .../test_functional_employee_permission.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 app/human_resources/tests/functional/employee/test_functional_employee_permission.py diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_permission.py b/app/human_resources/tests/functional/employee/test_functional_employee_permission.py new file mode 100644 index 00000000..63b22d6e --- /dev/null +++ b/app/human_resources/tests/functional/employee/test_functional_employee_permission.py @@ -0,0 +1,40 @@ +from access.tests.functional.contact.test_functional_contact_permission import ( + ContactPermissionsAPIInheritedCases +) + + + +class EmployeePermissionsAPITestCases( + ContactPermissionsAPIInheritedCases, +): + + add_data: dict = { + 'employee_number': 123456, + } + + kwargs_create_item: dict = { + 'employee_number': 1234568, + } + + kwargs_create_item_diff_org: dict = { + 'employee_number': 1234567, + } + + + +class EmployeePermissionsAPIInheritedCases( + EmployeePermissionsAPITestCases, +): + + add_data: dict = None + + kwargs_create_item: dict = None + + kwargs_create_item_diff_org: dict = None + + +class EmployeePermissionsAPIPyTest( + EmployeePermissionsAPITestCases, +): + + pass From ae6b66b27077a791ded1fa9ec4dbcdd07eb9c1ea Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:16:57 +0930 Subject: [PATCH 163/175] refactor(human_resources): Update Functional Serializer to use PyTest for Employee Model ref: #761 #730 --- .../test_functional_employee_serializer.py | 120 +++--------------- 1 file changed, 16 insertions(+), 104 deletions(-) diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py b/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py index 0fc35af7..e71756b9 100644 --- a/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py +++ b/app/human_resources/tests/functional/employee/test_functional_employee_serializer.py @@ -1,133 +1,45 @@ import pytest -from django.test import TestCase - from rest_framework.exceptions import ValidationError -from human_resources.serializers.entity_employee import ( - Employee, - ModelSerializer -) from access.tests.functional.contact.test_functional_contact_serializer import ( ContactSerializerInheritedCases ) -class SerializerTestCases( - ContactSerializerInheritedCases, +class EmployeeSerializerTestCases( + ContactSerializerInheritedCases ): - duplicate_f_name_l_name_dob = { - 'email': 'contactentityduplicateone@unit.test', - 'employee_number': 123456, + + parameterized_test_data: dict = { + "employee_number": { + 'will_create': False, + 'exception_key': 'required' + } } - kwargs_create_item: dict = { - 'email': 'ipfunny@unit.test', - 'employee_number': 1234567, - } - - kwargs_create_item_duplicate_f_name_l_name_dob = { - 'email': 'contactentityduplicatetwo@unit.test', - 'employee_number': 1234568, - } - - model = Employee - """Model to test""" - - create_model_serializer = ModelSerializer - """Serializer to test""" - valid_data: dict = { - 'email': 'ipweird@unit.test', - 'employee_number': 1234569, + 'employee_number': 123456, } + """Valid data used by serializer to create object""" - def test_serializer_validation_no_employee_number_exception(self): - """Serializer Validation Check - - Ensure that when creating with valid data and field employee_number is missing - a validation error occurs. - """ - - data = self.valid_data.copy() - - del data['employee_number'] - - with pytest.raises(ValidationError) as err: - - serializer = self.create_model_serializer( - data = data - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['employee_number'][0] == 'required' - - - -class ContactSerializerInheritedCases( - SerializerTestCases, +class EmployeeSerializerInheritedCases( + EmployeeSerializerTestCases, ): - create_model_serializer = None - """Serializer to test""" - - duplicate_f_name_l_name_dob: dict = None - """ Duplicate model serializer dict - - used for testing for duplicate f_name, l_name and dob fields. - """ - - kwargs_create_item: dict = None - """ Model kwargs to create item""" - - kwargs_create_item_duplicate_f_name_l_name_dob: dict = None - """model kwargs to create object - - **None:** Ensure that the fields of sub-model to person do not match - `self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown. - - used for testing for duplicate f_name, l_name and dob fields. - """ - - model = None - """Model to test""" + parameterized_test_data: dict = None valid_data: dict = None """Valid data used by serializer to create object""" - @classmethod - def setUpTestData(self): - """Setup Test""" - self.duplicate_f_name_l_name_dob.update( - super().duplicate_f_name_l_name_dob - ) - - self.kwargs_create_item_duplicate_f_name_l_name_dob.update( - super().kwargs_create_item_duplicate_f_name_l_name_dob - ) - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.valid_data.update( - super().valid_data - ) - - super().setUpTestData() - - - -class ContactSerializerTest( - SerializerTestCases, - TestCase, +class EmployeeSerializerPyTest( + EmployeeSerializerTestCases, ): - pass + parameterized_test_data: dict = None From 97e37f34a195fc74adb445c31299f1b5c682afe3 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:10 +0930 Subject: [PATCH 164/175] refactor(Access): Update Functional Serializer to use PyTest for Person Model ref: #761 #730 --- .../test_functional_person_serializer.py | 233 +++++------------- 1 file changed, 57 insertions(+), 176 deletions(-) diff --git a/app/access/tests/functional/person/test_functional_person_serializer.py b/app/access/tests/functional/person/test_functional_person_serializer.py index 180d8673..159b51b0 100644 --- a/app/access/tests/functional/person/test_functional_person_serializer.py +++ b/app/access/tests/functional/person/test_functional_person_serializer.py @@ -1,161 +1,88 @@ import pytest -from django.test import TestCase - from rest_framework.exceptions import ValidationError -from access.serializers.entity_person import ( - Person, - ModelSerializer -) from access.tests.functional.entity.test_functional_entity_serializer import ( + MockView, EntitySerializerInheritedCases ) -class SerializerTestCases( - EntitySerializerInheritedCases, +class PersonSerializerTestCases( + EntitySerializerInheritedCases ): - create_model_serializer = ModelSerializer - """Serializer to test""" - duplicate_f_name_l_name_dob: dict = { - 'f_name': 'fred', - 'm_name': 'D', - 'l_name': 'Flinstone', - 'dob': '2025-04-08', + parameterized_test_data: dict = { + "model_notes": { + 'will_create': True, + }, + "f_name": { + 'will_create': False, + 'exception_key': 'required' + }, + "m_name": { + 'will_create': True, + }, + "l_name": { + 'will_create': False, + 'exception_key': 'required' + }, + "dob": { + 'will_create': True, + } } - kwargs_create_item_duplicate_f_name_l_name_dob: dict = { - 'f_name': 'fred', - 'm_name': 'D', - 'l_name': 'Flinstone', - 'dob': '2025-04-08', - } - - kwargs_create_item: dict = { + valid_data: dict = { 'f_name': 'Ian', 'm_name': 'Peter', 'l_name': 'Funny', 'dob': '2025-04-08', } - - model = Person - """Model to test""" - - valid_data: dict = { - 'f_name': 'Ian', - 'm_name': 'Peter', - 'l_name': 'Strange', - 'dob': '2025-04-08', - } + """Valid data used by serializer to create object""" - - def test_serializer_validation_no_f_name_exception(self): - """Serializer Validation Check - - Ensure that when creating with valid data and field f_name is missing - a validation error occurs. - """ - - data = self.valid_data.copy() - - del data['f_name'] - - with pytest.raises(ValidationError) as err: - - serializer = self.create_model_serializer( - data = data - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['f_name'][0] == 'required' - - - - def test_serializer_validation_no_m_name(self): - """Serializer Validation Check - - Ensure that when creating with valid data and field f_name is missing - no validation error occurs. - """ - - data = self.valid_data.copy() - - del data['m_name'] - - serializer = self.create_model_serializer( - data = data - ) - - assert serializer.is_valid(raise_exception = True) - - - - def test_serializer_validation_no_l_name_exception(self): - """Serializer Validation Check - - Ensure that when creating with valid data and field l_name is missing - a validation error occurs. - """ - - data = self.valid_data.copy() - - del data['l_name'] - - with pytest.raises(ValidationError) as err: - - serializer = self.create_model_serializer( - data = data - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['l_name'][0] == 'required' - - - - def test_serializer_validation_no_dob(self): - """Serializer Validation Check - - Ensure that when creating with valid data and field dob is missing - no validation error occurs. - """ - - data = self.valid_data.copy() - - del data['dob'] - - serializer = self.create_model_serializer( - data = data - ) - - assert serializer.is_valid(raise_exception = True) - - - - def test_serializer_validation_duplicate_f_name_l_name_dob(self): + def test_serializer_validation_duplicate_f_name_l_name_dob(self, model, create_serializer): """Serializer Validation Check Ensure that when creating with valid data and fields f_name, l_name and dob already exists in the db a validation error occurs. """ - self.model.objects.create( - organization = self.organization, - **self.kwargs_create_item_duplicate_f_name_l_name_dob + valid_data = self.valid_data.copy() + + valid_data['f_name'] = 'duplicate' + + valid_data['organization'] = self.organization + + obj = model.objects.create( + **valid_data ) - data = self.duplicate_f_name_l_name_dob.copy() + valid_data['organization'] = self.organization.id + + if 'email' in valid_data: # Contact Entity + + valid_data['email'] = 'abc@xyz.qwe' + + if 'name' in valid_data: # Company Entity + + valid_data['name'] = 'diff' + + if 'employee_number' in valid_data: # Employee Entity + + valid_data['employee_number'] = 13579 + + view_set = MockView() with pytest.raises(ValidationError) as err: - serializer = self.create_model_serializer( - data = data + serializer = create_serializer( + context = { + 'view': view_set, + }, + data = valid_data ) serializer.is_valid(raise_exception = True) @@ -167,64 +94,18 @@ class SerializerTestCases( class PersonSerializerInheritedCases( - SerializerTestCases, + PersonSerializerTestCases, ): - create_model_serializer = None - """Serializer to test""" - - duplicate_f_name_l_name_dob: dict = None - """ Duplicate model serializer dict - - used for testing for duplicate f_name, l_name and dob fields. - """ - - kwargs_create_item: dict = None - """ Model kwargs to create item""" - - kwargs_create_item_duplicate_f_name_l_name_dob: dict = None - """model kwargs to create object - - **None:** Ensure that the fields of sub-model to person do not match - `self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown. - - used for testing for duplicate f_name, l_name and dob fields. - """ - - model = None - """Model to test""" + parameterized_test_data: dict = None valid_data: dict = None """Valid data used by serializer to create object""" - @classmethod - def setUpTestData(self): - """Setup Test""" - self.duplicate_f_name_l_name_dob.update( - super().duplicate_f_name_l_name_dob - ) - - self.kwargs_create_item_duplicate_f_name_l_name_dob.update( - super().kwargs_create_item_duplicate_f_name_l_name_dob - ) - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.valid_data.update( - super().valid_data - ) - - super().setUpTestData() - - - -class PersonSerializerTest( - SerializerTestCases, - TestCase, +class PersonSerializerPyTest( + PersonSerializerTestCases, ): - pass + parameterized_test_data: dict = None From 254cd02649a6715f2adc8369068efeb98f09ee3e Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:21 +0930 Subject: [PATCH 165/175] refactor(Access): Update Functional Serializer to use PyTest for Entity Model ref: #761 #730 --- .../test_functional_entity_serializer.py | 209 +++++++++++++----- 1 file changed, 158 insertions(+), 51 deletions(-) diff --git a/app/access/tests/functional/entity/test_functional_entity_serializer.py b/app/access/tests/functional/entity/test_functional_entity_serializer.py index 6b6e8c01..95348bb2 100644 --- a/app/access/tests/functional/entity/test_functional_entity_serializer.py +++ b/app/access/tests/functional/entity/test_functional_entity_serializer.py @@ -1,94 +1,171 @@ +import django import pytest -from django.test import TestCase +from rest_framework.exceptions import ValidationError -from access.models.tenant import Tenant as Organization -from access.serializers.entity import ( - Entity, - ModelSerializer -) +User = django.contrib.auth.get_user_model() -class SerializerTestCases: +class MockView: - kwargs_create_item: dict = {} - """ Model kwargs to create item""" + _has_import: bool = False + """User Permission - model = Entity - """Model to test""" + get_permission_required() sets this to `True` when user has import permission. + """ - create_model_serializer = ModelSerializer - """Serializer to test""" + _has_purge: bool = False + """User Permission - valid_data: dict = {} + get_permission_required() sets this to `True` when user has purge permission. + """ + + _has_triage: bool = False + """User Permission + + get_permission_required() sets this to `True` when user has triage permission. + """ + + + +class EntitySerializerTestCases: + + + parameterized_test_data: dict = { + "model_notes": { + 'will_create': True, + } + } + + valid_data: dict = { + 'model_notes': 'model notes field' + } """Valid data used by serializer to create object""" - @classmethod - def setUpTestData(self): - """Setup Test""" - - self.organization = Organization.objects.create(name='test_org') - self.kwargs_create_item.update({ - 'model_notes': 'model notes field' - }) + @pytest.fixture( scope = 'class') + def setup_data(self, + request, + model, + django_db_blocker, + organization_one, + ): - self.valid_data.update({ - 'organization': self.organization.pk, - 'model_notes': 'model notes field' - }) + with django_db_blocker.unblock(): - self.item = self.model.objects.create( - organization = self.organization, - **self.kwargs_create_item, - ) + request.cls.organization = organization_one + + valid_data = {} + + for base in reversed(request.cls.__mro__): + + if hasattr(base, 'valid_data'): + + if base.valid_data is None: + + continue + + valid_data.update(**base.valid_data) + + + if len(valid_data) > 0: + + request.cls.valid_data = valid_data + + + if 'organization' not in request.cls.valid_data: + + request.cls.valid_data.update({ + 'organization': request.cls.organization.pk + }) + + + request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password") + + + yield + + with django_db_blocker.unblock(): + + request.cls.view_user.delete() + + del request.cls.valid_data - def test_serializer_valid_data(self): + @pytest.fixture( scope = 'class', autouse = True) + def class_setup(self, + setup_data, + ): + + pass + + + def test_serializer_valid_data(self, create_serializer): """Serializer Validation Check Ensure that when creating an object with valid data, no validation error occurs. """ - serializer = self.create_model_serializer( + view_set = MockView() + + serializer = create_serializer( + context = { + 'view': view_set, + }, data = self.valid_data ) assert serializer.is_valid(raise_exception = True) - def test_serializer_validation_no_model_notes(self): + + def test_serializer_valid_data_missing_field_is_valid(self, parameterized, param_key_test_data, + create_serializer, + param_value, + param_will_create, + ): """Serializer Validation Check - Ensure that if creating and no model_notes is provided no validation - error occurs + Ensure that when creating an object with a user with import permission + and with valid data, no validation error occurs. """ - data = self.valid_data.copy() - - del data['model_notes'] + valid_data = self.valid_data.copy() - serializer = self.create_model_serializer( - data = data + del valid_data[param_value] + + view_set = MockView() + + view_set._has_import = True + + serializer = create_serializer( + context = { + 'view': view_set, + }, + data = valid_data ) - assert serializer.is_valid(raise_exception = True) + is_valid = serializer.is_valid(raise_exception = False) + + assert ( + ( + not param_will_create + and param_will_create == is_valid + ) + or param_will_create == is_valid + ) class EntitySerializerInheritedCases( - SerializerTestCases, + EntitySerializerTestCases, ): - create_model_serializer = None - """Serializer to test""" - - kwargs_create_item: dict = None - """ Model kwargs to create item""" + parameterized_test_data: dict = None model = None """Model to test""" @@ -97,10 +174,40 @@ class EntitySerializerInheritedCases( """Valid data used by serializer to create object""" + def test_serializer_valid_data_missing_field_raises_exception(self, parameterized, param_key_test_data, + create_serializer, + param_value, + param_exception_key, + ): + """Serializer Validation Check -class EntitySerializerTest( - SerializerTestCases, - TestCase, + Ensure that when creating an object with a user with import permission + and with valid data, no validation error occurs. + """ + + valid_data = self.valid_data.copy() + + del valid_data[param_value] + + view_set = MockView() + + with pytest.raises(ValidationError) as err: + + serializer = create_serializer( + context = { + 'view': view_set, + }, + data = valid_data + ) + + is_valid = serializer.is_valid(raise_exception = True) + + assert err.value.get_codes()[param_value][0] == param_exception_key + + + +class EntitySerializerPyTest( + EntitySerializerTestCases, ): - pass + parameterized_test_data: dict = None From 3505f915c5ba0b0d5992d234363be1ec84aa7b36 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:31 +0930 Subject: [PATCH 166/175] refactor(Access): Update Functional Serializer to use PyTest for Contact Model ref: #761 #730 --- .../test_functional_contact_serializer.py | 115 +++--------------- 1 file changed, 16 insertions(+), 99 deletions(-) diff --git a/app/access/tests/functional/contact/test_functional_contact_serializer.py b/app/access/tests/functional/contact/test_functional_contact_serializer.py index c4b278d3..73c6dc5f 100644 --- a/app/access/tests/functional/contact/test_functional_contact_serializer.py +++ b/app/access/tests/functional/contact/test_functional_contact_serializer.py @@ -1,129 +1,46 @@ import pytest -from django.test import TestCase - from rest_framework.exceptions import ValidationError -from access.serializers.entity_contact import ( - Contact, - ModelSerializer -) from access.tests.functional.person.test_functional_person_serializer import ( + MockView, PersonSerializerInheritedCases ) -class SerializerTestCases( - PersonSerializerInheritedCases, +class ContactSerializerTestCases( + PersonSerializerInheritedCases ): - duplicate_f_name_l_name_dob = { - 'email': 'contactentityduplicateone@unit.test', + + parameterized_test_data: dict = { + "email": { + 'will_create': False, + 'exception_key': 'required' + } } - kwargs_create_item: dict = { - 'email': 'ipfunny@unit.test', - } - - kwargs_create_item_duplicate_f_name_l_name_dob = { - 'email': 'contactentityduplicatetwo@unit.test', - } - - model = Contact - """Model to test""" - - create_model_serializer = ModelSerializer - """Serializer to test""" - valid_data: dict = { - 'email': 'ipweird@unit.test', + 'email': 'contactentityduplicatetwo@unit.test', } - - - - def test_serializer_validation_no_email_exception(self): - """Serializer Validation Check - - Ensure that when creating with valid data and field email is missing - a validation error occurs. - """ - - data = self.valid_data.copy() - - del data['email'] - - with pytest.raises(ValidationError) as err: - - serializer = self.create_model_serializer( - data = data - ) - - serializer.is_valid(raise_exception = True) - - assert err.value.get_codes()['email'][0] == 'required' + """Valid data used by serializer to create object""" class ContactSerializerInheritedCases( - SerializerTestCases, + ContactSerializerTestCases, ): - create_model_serializer = None - """Serializer to test""" - - duplicate_f_name_l_name_dob: dict = None - """ Duplicate model serializer dict - - used for testing for duplicate f_name, l_name and dob fields. - """ - - kwargs_create_item: dict = None - """ Model kwargs to create item""" - - kwargs_create_item_duplicate_f_name_l_name_dob: dict = None - """model kwargs to create object - - **None:** Ensure that the fields of sub-model to person do not match - `self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown. - - used for testing for duplicate f_name, l_name and dob fields. - """ - - model = None - """Model to test""" + parameterized_test_data: dict = None valid_data: dict = None """Valid data used by serializer to create object""" - @classmethod - def setUpTestData(self): - """Setup Test""" - self.duplicate_f_name_l_name_dob.update( - super().duplicate_f_name_l_name_dob - ) - - self.kwargs_create_item_duplicate_f_name_l_name_dob.update( - super().kwargs_create_item_duplicate_f_name_l_name_dob - ) - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.valid_data.update( - super().valid_data - ) - - super().setUpTestData() - - - -class ContactSerializerTest( - SerializerTestCases, - TestCase, +class ContactSerializerPyTest( + ContactSerializerTestCases, ): - pass + parameterized_test_data: dict = None From 2e096cb495bc6781d56b21571df57a20cfa14f06 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:44 +0930 Subject: [PATCH 167/175] refactor(Access): Update Functional Permission to use PyTest for Contact Model ref: #761 #730 --- .../test_functional_contact_permission.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/access/tests/functional/contact/test_functional_contact_permission.py diff --git a/app/access/tests/functional/contact/test_functional_contact_permission.py b/app/access/tests/functional/contact/test_functional_contact_permission.py new file mode 100644 index 00000000..96811ef7 --- /dev/null +++ b/app/access/tests/functional/contact/test_functional_contact_permission.py @@ -0,0 +1,70 @@ +import pytest + +from access.tests.functional.person.test_functional_person_permission import ( + PersonPermissionsAPIInheritedCases +) + + + +class ContactPermissionsAPITestCases( + PersonPermissionsAPIInheritedCases, +): + + add_data: dict = { + 'email': 'ipfunny@unit.test', + } + + kwargs_create_item: dict = { + 'email': 'ipweird@unit.test', + } + + kwargs_create_item_diff_org: dict = { + 'email': 'ipstrange@unit.test', + } + + + +class ContactPermissionsAPIInheritedCases( + ContactPermissionsAPITestCases, +): + + add_data: dict = None + + kwargs_create_item: dict = None + + kwargs_create_item_diff_org: dict = None + + # url_name = '_api_v2_entity_sub' + + + # @pytest.fixture(scope='class') + # def inherited_var_setup(self, request): + + # request.cls.url_kwargs.update({ + # 'entity_model': self.model._meta.sub_model_type + # }) + + # request.cls.url_view_kwargs.update({ + # 'entity_model': self.model._meta.sub_model_type + # }) + + + + # @pytest.fixture(scope='class', autouse = True) + # def class_setup(self, request, django_db_blocker, + # model, + # var_setup, + # prepare, + # inherited_var_setup, + # diff_org_model, + # create_model, + # ): + + # pass + + +class ContactPermissionsAPIPyTest( + ContactPermissionsAPITestCases, +): + + pass From 516d6fc1364644d9cbe493ad02b040fce45a7c00 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:17:52 +0930 Subject: [PATCH 168/175] refactor(Access): Update Functional Permission to use PyTest for Entity Model ref: #761 #730 --- .../test_functional_entity_permission.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 app/access/tests/functional/entity/test_functional_entity_permission.py diff --git a/app/access/tests/functional/entity/test_functional_entity_permission.py b/app/access/tests/functional/entity/test_functional_entity_permission.py new file mode 100644 index 00000000..4f725f47 --- /dev/null +++ b/app/access/tests/functional/entity/test_functional_entity_permission.py @@ -0,0 +1,89 @@ +import pytest + +from api.tests.functional.test_functional_api_permissions import ( + APIPermissionsInheritedCases, +) + + + +class EntityPermissionsAPITestCases( + APIPermissionsInheritedCases, +): + + add_data: dict = {} + + app_namespace = 'v2' + + change_data = {} + + delete_data = {} + + kwargs_create_item: dict = {} + + kwargs_create_item_diff_org: dict = {} + + url_kwargs: dict = {} + + url_name = '_api_v2_entity' + + url_view_kwargs: dict = {} + + + + def test_returned_data_from_user_and_global_organizations_only(self): + """Check items returned + + This test case is a over-ride of a test case with the same name. + This model is not a tenancy model making this test not-applicable. + + Items returned from the query Must be from the users organization and + global ONLY! + """ + pass + + + +class EntityPermissionsAPIInheritedCases( + EntityPermissionsAPITestCases, +): + + add_data: dict = None + + kwargs_create_item: dict = None + + kwargs_create_item_diff_org: dict = None + + url_name = '_api_v2_entity_sub' + + + @pytest.fixture(scope='class') + def inherited_var_setup(self, request): + + request.cls.url_kwargs.update({ + 'entity_model': self.model._meta.sub_model_type + }) + + request.cls.url_view_kwargs.update({ + 'entity_model': self.model._meta.sub_model_type + }) + + + + @pytest.fixture(scope='class', autouse = True) + def class_setup(self, request, django_db_blocker, + model, + var_setup, + prepare, + inherited_var_setup, + diff_org_model, + create_model, + ): + + pass + + +class EntityPermissionsAPIPyTest( + EntityPermissionsAPITestCases, +): + + pass From 8bba04305fe9d983e89aafc5dac7c3d811307796 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 02:18:00 +0930 Subject: [PATCH 169/175] refactor(Access): Update Functional Permission to use PyTest for Person Model ref: #761 #730 --- .../test_functional_person_permission.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 app/access/tests/functional/person/test_functional_person_permission.py diff --git a/app/access/tests/functional/person/test_functional_person_permission.py b/app/access/tests/functional/person/test_functional_person_permission.py new file mode 100644 index 00000000..983a4f01 --- /dev/null +++ b/app/access/tests/functional/person/test_functional_person_permission.py @@ -0,0 +1,91 @@ +import pytest + +from access.tests.functional.entity.test_functional_entity_permission import ( + EntityPermissionsAPIInheritedCases +) + + + +class PersonPermissionsAPITestCases( + EntityPermissionsAPIInheritedCases, +): + + add_data: dict = { + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Strange', + 'dob': '2025-04-08', + } + + # app_namespace = 'v2' + + # change_data = {} + + # delete_data = {} + + kwargs_create_item: dict = { + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Weird', + 'dob': '2025-04-08', + } + + kwargs_create_item_diff_org: dict = { + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Funny', + 'dob': '2025-04-08', + } + + # url_kwargs: dict = {} + + # url_name = '_api_v2_entity' + + # url_view_kwargs: dict = {} + + + +class PersonPermissionsAPIInheritedCases( + PersonPermissionsAPITestCases, +): + + add_data: dict = None + + kwargs_create_item: dict = None + + kwargs_create_item_diff_org: dict = None + + # url_name = '_api_v2_entity_sub' + + + # @pytest.fixture(scope='class') + # def inherited_var_setup(self, request): + + # request.cls.url_kwargs.update({ + # 'entity_model': self.model._meta.sub_model_type + # }) + + # request.cls.url_view_kwargs.update({ + # 'entity_model': self.model._meta.sub_model_type + # }) + + + + # @pytest.fixture(scope='class', autouse = True) + # def class_setup(self, request, django_db_blocker, + # model, + # var_setup, + # prepare, + # inherited_var_setup, + # diff_org_model, + # create_model, + # ): + + # pass + + +class PersonPermissionsAPIPyTest( + PersonPermissionsAPITestCases, +): + + pass From a4d1a2bf76a7d723a88fc4bc0611ef4663ae31e8 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:07:28 +0930 Subject: [PATCH 170/175] test(access): Functional ViewSet Test Suite Company model ref: #761 #760 --- .../test_functional_company_viewset.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 app/access/tests/functional/company/test_functional_company_viewset.py diff --git a/app/access/tests/functional/company/test_functional_company_viewset.py b/app/access/tests/functional/company/test_functional_company_viewset.py new file mode 100644 index 00000000..253f6fb2 --- /dev/null +++ b/app/access/tests/functional/company/test_functional_company_viewset.py @@ -0,0 +1,58 @@ +from django.test import TestCase + +from access.models.company_base import Company +from access.tests.functional.entity.test_functional_entity_viewset import ( + EntityViewSetInheritedCases +) + + + +class ViewSetTestCases( + EntityViewSetInheritedCases, +): + + add_data: dict = { + 'name': 'Ian', + } + + kwargs_create_item: dict = { + 'name': 'Ian2', + } + + kwargs_create_item_diff_org: dict = { + 'name': 'Ian3', + } + + model = Company + + + +class CompanyViewSetInheritedCases( + ViewSetTestCases, +): + + model = None + + + @classmethod + def setUpTestData(self): + + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + + super().setUpTestData() + + + +class CompanyViewSetTest( + ViewSetTestCases, + TestCase, +): + pass From c39f479b96955973deeacc77b4c25a27eba3311d Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:07:46 +0930 Subject: [PATCH 171/175] refactor(Access): Update Functional ViewSet to use PyTest for Contact Model ref: #761 #730 --- .../test_functional_contact_viewset.py | 153 +++--------------- 1 file changed, 21 insertions(+), 132 deletions(-) diff --git a/app/access/tests/functional/contact/test_functional_contact_viewset.py b/app/access/tests/functional/contact/test_functional_contact_viewset.py index d7754f8d..1f52aca1 100644 --- a/app/access/tests/functional/contact/test_functional_contact_viewset.py +++ b/app/access/tests/functional/contact/test_functional_contact_viewset.py @@ -2,91 +2,28 @@ from django.test import TestCase from access.models.contact import Contact from access.tests.functional.person.test_functional_person_viewset import ( - PersonMetadataInheritedCases, - PersonPermissionsAPIInheritedCases, PersonViewSetInheritedCases ) -class ViewSetBase: - - add_data = { - 'email': 'ipfunny@unit.test', - } - - kwargs_create_item_diff_org = { - 'email': 'ipstrange@unit.test', - } - - kwargs_create_item = { - 'email': 'ipweird@unit.test', - } - - model = Contact - - url_kwargs: dict = {} - - url_view_kwargs: dict = {} - - - -class PermissionsAPITestCases( - ViewSetBase, - PersonPermissionsAPIInheritedCases, -): - - pass - - - -class ContactPermissionsAPIInheritedCases( - PermissionsAPITestCases, -): - - add_data: dict = None - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - - @classmethod - def setUpTestData(self): - - self.add_data.update( - super().add_data - ) - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) - - super().setUpTestData() - - - -class ContactPermissionsAPITest( - PermissionsAPITestCases, - TestCase, -): - - pass - - - class ViewSetTestCases( - ViewSetBase, PersonViewSetInheritedCases, ): - pass + add_data: dict = { + 'email': 'ipfunny@unit.test', + } + + kwargs_create_item: dict = { + 'email': 'ipweird@unit.test', + } + + kwargs_create_item_diff_org: dict = { + 'email': 'ipstrange@unit.test', + } + + model = Contact @@ -96,21 +33,19 @@ class ContactViewSetInheritedCases( model = None - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - @classmethod def setUpTestData(self): - self.kwargs_create_item.update( - super().kwargs_create_item - ) + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } super().setUpTestData() @@ -120,50 +55,4 @@ class ContactViewSetTest( ViewSetTestCases, TestCase, ): - - pass - - - -class MetadataTestCases( - ViewSetBase, - PersonMetadataInheritedCases, -): - - pass - - - -class ContactMetadataInheritedCases( - MetadataTestCases, -): - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - - @classmethod - def setUpTestData(self): - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) - - super().setUpTestData() - - - -class ContactMetadataTest( - MetadataTestCases, - TestCase, - -): - pass From f1016bd9cc7b7547262a36ad9de66770372b6e83 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:07:57 +0930 Subject: [PATCH 172/175] refactor(Access): Update Functional ViewSet to use PyTest for Entity Model ref: #761 #730 --- .../entity/test_functional_entity_viewset.py | 324 +++--------------- 1 file changed, 41 insertions(+), 283 deletions(-) diff --git a/app/access/tests/functional/entity/test_functional_entity_viewset.py b/app/access/tests/functional/entity/test_functional_entity_viewset.py index e0445551..7e822b48 100644 --- a/app/access/tests/functional/entity/test_functional_entity_viewset.py +++ b/app/access/tests/functional/entity/test_functional_entity_viewset.py @@ -1,5 +1,4 @@ import django - from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.test import TestCase @@ -9,8 +8,6 @@ from access.models.tenant import Tenant as Organization from access.models.team import Team from access.models.team_user import TeamUsers -from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional -from api.tests.abstract.api_permissions_viewset import APIPermissions from api.tests.abstract.api_serializer_viewset import SerializersTestCases User = django.contrib.auth.get_user_model() @@ -19,23 +16,34 @@ User = django.contrib.auth.get_user_model() class ViewSetBase: - add_data: dict = None + add_data: dict = { + 'model_notes': 'added model note' + } app_namespace = 'v2' + base_model = Entity + """Base model for this sub model + don't change or override this value + """ + change_data = None delete_data = {} - kwargs_create_item: dict = {} + kwargs_create_item: dict = { + 'model_notes': 'added model note' + } - kwargs_create_item_diff_org: dict = {} + kwargs_create_item_diff_org: dict = { + 'model_notes': 'added model note' + } model = None - url_kwargs: dict = None + url_kwargs: dict = {} - url_view_kwargs: dict = None + url_view_kwargs: dict = {} url_name = None @@ -57,16 +65,15 @@ class ViewSetBase: self.different_organization = Organization.objects.create(name='test_different_organization') + self.view_user = User.objects.create_user(username="test_user_view", password="password") self.item = self.model.objects.create( organization = organization, - model_notes = 'some notes', **self.kwargs_create_item ) self.other_org_item = self.model.objects.create( organization = self.different_organization, - model_notes = 'some more notes', **self.kwargs_create_item_diff_org ) @@ -75,7 +82,9 @@ class ViewSetBase: if self.add_data is not None: - self.add_data.update({'organization': self.organization.id}) + self.add_data.update({ + 'organization': self.organization.id, + }) view_permissions = Permission.objects.get( @@ -148,7 +157,6 @@ class ViewSetBase: self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password") - self.view_user = User.objects.create_user(username="test_user_view", password="password") TeamUsers.objects.create( team = view_team, user = self.view_user @@ -194,98 +202,16 @@ class ViewSetBase: ) - -class PermissionsAPITestCases( - ViewSetBase, - APIPermissions, -): - - - add_data: dict = {} - - change_data = {'model_notes': 'device'} - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - url_kwargs: dict = None - - url_view_kwargs: dict = None - - url_name = None - - - @classmethod - def setUpTestData(self): - - self.add_data.update({ 'model_note': 'added model note' }) - - super().setUpTestData() - - - def test_returned_data_from_user_and_global_organizations_only(self): - """Check items returned - - This test case is a over-ride of a test case with the same name. - This model is not a tenancy model making this test not-applicable. - - Items returned from the query Must be from the users organization and - global ONLY! + def test_sanity_is_asset_sub_model(self): + """Sanity Test + + This test ensures that the model being tested `self.model` is a + sub-model of `self.base_model`. + This test is required as the same viewset is used for all sub-models + of `Entity` """ - pass - - - -class EntityPermissionsAPIInheritedCases( - PermissionsAPITestCases, -): - - add_data: dict = None - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - url_name = '_api_v2_entity_sub' - - - @classmethod - def setUpTestData(self): - - self.url_kwargs = { - 'entity_model': self.model._meta.model_name - } - - self.url_view_kwargs = { - 'entity_model': self.model._meta.model_name - } - - super().setUpTestData() - - - -class EntityPermissionsAPITest( - PermissionsAPITestCases, - TestCase, -): - - kwargs_create_item: dict = {} - - kwargs_create_item_diff_org: dict = {} - - model = Entity - - url_kwargs: dict = {} - - url_view_kwargs: dict = {} - - url_name = '_api_v2_entity' + assert issubclass(self.model, self.base_model) @@ -294,17 +220,7 @@ class ViewSetTestCases( SerializersTestCases, ): - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - model = None - - url_kwargs: dict = None - - url_view_kwargs: dict = None - - url_name = None + model = Entity @@ -314,22 +230,28 @@ class EntityViewSetInheritedCases( model = None - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - url_name = '_api_v2_entity_sub' @classmethod def setUpTestData(self): + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } + + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } + self.url_kwargs = { - 'entity_model': self.model._meta.model_name + 'entity_model': self.model._meta.sub_model_type } self.url_view_kwargs = { - 'entity_model': self.model._meta.model_name + 'entity_model': self.model._meta.sub_model_type } super().setUpTestData() @@ -341,168 +263,4 @@ class EntityViewSetTest( TestCase, ): - kwargs_create_item: dict = {} - - kwargs_create_item_diff_org: dict = {} - - model = Entity - - url_kwargs: dict = {} - - url_view_kwargs: dict = {} - url_name = '_api_v2_entity' - - - -class MetadataTestCases( - ViewSetBase, - MetadataAttributesFunctional, -): - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - model = None - - url_kwargs: dict = None - - url_view_kwargs: dict = None - - url_name = None - - - -class EntityMetadataInheritedCases( - MetadataTestCases, -): - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - url_name = '_api_v2_entity_sub' - - - @classmethod - def setUpTestData(self): - - self.url_kwargs = { - 'entity_model': self.model._meta.model_name - } - - self.url_view_kwargs = { - 'entity_model': self.model._meta.model_name - } - - super().setUpTestData() - - - -class EntityMetadataTest( - MetadataTestCases, - TestCase, - -): - - kwargs_create_item: dict = {} - - kwargs_create_item_diff_org: dict = {} - - model = Entity - - url_kwargs: dict = {} - - url_view_kwargs: dict = {} - - url_name = '_api_v2_entity' - - - # def test_method_options_request_detail_data_has_key_urls_back(self): - # """Test HTTP/Options Method - - # Ensure the request data returned has key `urls.back` - # """ - - # client = Client() - # client.force_login(self.view_user) - - # response = client.options( - # reverse( - # self.app_namespace + ':' + self.url_name + '-detail', - # kwargs=self.url_view_kwargs - # ), - # content_type='application/json' - # ) - - # assert 'back' in response.data['urls'] - - - # def test_method_options_request_detail_data_key_urls_back_is_str(self): - # """Test HTTP/Options Method - - # Ensure the request data key `urls.back` is str - # """ - - # client = Client() - # client.force_login(self.view_user) - - # response = client.options( - # reverse( - # self.app_namespace + ':' + self.url_name + '-detail', - # kwargs=self.url_view_kwargs - # ), - # content_type='application/json' - # ) - - # assert type(response.data['urls']['back']) is str - - - - # def test_method_options_request_list_data_has_key_urls_return_url(self): - # """Test HTTP/Options Method - - # Ensure the request data returned has key `urls.return_url` - # """ - - # client = Client() - # client.force_login(self.view_user) - - # if self.url_kwargs: - - # url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs) - - # else: - - # url = reverse(self.app_namespace + ':' + self.url_name + '-list') - - # response = client.options( url, content_type='application/json' ) - - # assert 'return_url' in response.data['urls'] - - - # def test_method_options_request_list_data_key_urls_return_url_is_str(self): - # """Test HTTP/Options Method - - # Ensure the request data key `urls.return_url` is str - # """ - - # client = Client() - # client.force_login(self.view_user) - - # if self.url_kwargs: - - # url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs) - - # else: - - # url = reverse(self.app_namespace + ':' + self.url_name + '-list') - - # response = client.options( url, content_type='application/json' ) - - # assert type(response.data['urls']['return_url']) is str - - From 07277862cf51649f44ed522ff742b8850f531967 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:08:05 +0930 Subject: [PATCH 173/175] refactor(Access): Update Functional ViewSet to use PyTest for Person Model ref: #761 #730 --- .../tests/functional/person/conftest.py | 24 +++ .../person/test_functional_person_viewset.py | 151 +++--------------- 2 files changed, 44 insertions(+), 131 deletions(-) create mode 100644 app/access/tests/functional/person/conftest.py diff --git a/app/access/tests/functional/person/conftest.py b/app/access/tests/functional/person/conftest.py new file mode 100644 index 00000000..77450c9a --- /dev/null +++ b/app/access/tests/functional/person/conftest.py @@ -0,0 +1,24 @@ +import pytest + +from access.models.person import Person + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Person + + yield request.cls.model + + del request.cls.model + + + +@pytest.fixture(scope='function') +def create_serializer(): + + from access.serializers.entity_person import ModelSerializer + + + yield ModelSerializer diff --git a/app/access/tests/functional/person/test_functional_person_viewset.py b/app/access/tests/functional/person/test_functional_person_viewset.py index bda095e0..3373092b 100644 --- a/app/access/tests/functional/person/test_functional_person_viewset.py +++ b/app/access/tests/functional/person/test_functional_person_viewset.py @@ -2,101 +2,38 @@ from django.test import TestCase from access.models.person import Person from access.tests.functional.entity.test_functional_entity_viewset import ( - EntityMetadataInheritedCases, - EntityPermissionsAPIInheritedCases, EntityViewSetInheritedCases ) -class ViewSetBase: +class ViewSetTestCases( + EntityViewSetInheritedCases, +): - add_data = { + add_data: dict = { 'f_name': 'Ian', 'm_name': 'Peter', 'l_name': 'Strange', 'dob': '2025-04-08', } - kwargs_create_item_diff_org = { - 'f_name': 'Ian', - 'm_name': 'Peter', - 'l_name': 'Funny', - 'dob': '2025-04-08', - } - - kwargs_create_item = { + kwargs_create_item: dict = { 'f_name': 'Ian', 'm_name': 'Peter', 'l_name': 'Weird', 'dob': '2025-04-08', } + kwargs_create_item_diff_org: dict = { + 'f_name': 'Ian', + 'm_name': 'Peter', + 'l_name': 'Funny', + 'dob': '2025-04-08', + } + model = Person - url_kwargs: dict = {} - - url_view_kwargs: dict = {} - - - -class PermissionsAPITestCases( - ViewSetBase, - EntityPermissionsAPIInheritedCases, -): - - pass - - - -class PersonPermissionsAPIInheritedCases( - PermissionsAPITestCases, -): - - add_data: dict = None - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - - @classmethod - def setUpTestData(self): - - self.add_data.update( - super().add_data - ) - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) - - super().setUpTestData() - - - -class PersonPermissionsAPITest( - PermissionsAPITestCases, - TestCase, -): - - pass - - - -class ViewSetTestCases( - ViewSetBase, - EntityViewSetInheritedCases, -): - - pass - class PersonViewSetInheritedCases( @@ -105,21 +42,19 @@ class PersonViewSetInheritedCases( model = None - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - @classmethod def setUpTestData(self): - self.kwargs_create_item.update( - super().kwargs_create_item - ) + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } super().setUpTestData() @@ -129,50 +64,4 @@ class PersonViewSetTest( ViewSetTestCases, TestCase, ): - - pass - - - -class MetadataTestCases( - ViewSetBase, - EntityMetadataInheritedCases, -): - - pass - - - -class PersonMetadataInheritedCases( - MetadataTestCases, -): - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - - @classmethod - def setUpTestData(self): - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) - - super().setUpTestData() - - - -class PersonMetadataTest( - MetadataTestCases, - TestCase, - -): - pass From 1663f19b2a1f3464e83d2b01d58fae4b0e166476 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 03:10:00 +0930 Subject: [PATCH 174/175] refactor(human_resources): Update Functional ViewSet to use PyTest for Employee Model ref: #761 #730 --- .../tests/functional/employee/conftest.py | 24 +++ .../test_functional_employee_viewset.py | 157 +++--------------- 2 files changed, 46 insertions(+), 135 deletions(-) create mode 100644 app/human_resources/tests/functional/employee/conftest.py diff --git a/app/human_resources/tests/functional/employee/conftest.py b/app/human_resources/tests/functional/employee/conftest.py new file mode 100644 index 00000000..883bcdca --- /dev/null +++ b/app/human_resources/tests/functional/employee/conftest.py @@ -0,0 +1,24 @@ +import pytest + +from human_resources.models.employee import Employee + + + +@pytest.fixture( scope = 'class') +def model(request): + + request.cls.model = Employee + + yield request.cls.model + + del request.cls.model + + + +@pytest.fixture(scope='function') +def create_serializer(): + + from human_resources.serializers.entity_employee import ModelSerializer + + + yield ModelSerializer diff --git a/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py b/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py index 978017aa..1fb3149b 100644 --- a/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py +++ b/app/human_resources/tests/functional/employee/test_functional_employee_viewset.py @@ -1,8 +1,7 @@ from django.test import TestCase + from access.tests.functional.contact.test_functional_contact_viewset import ( - ContactMetadataInheritedCases, - ContactPermissionsAPIInheritedCases, ContactViewSetInheritedCases ) @@ -10,87 +9,23 @@ from human_resources.models.employee import Employee -class ViewSetBase: - - add_data = { - 'email': 'ipfunny@unit.test', - 'employee_number': 123456, - } - - kwargs_create_item_diff_org = { - 'email': 'ipstrange@unit.test', - 'employee_number': 1234567, - } - - kwargs_create_item = { - 'email': 'ipweird@unit.test', - 'employee_number': 1234568, - } - - model = Employee - - url_kwargs: dict = {} - - url_view_kwargs: dict = {} - - - -class PermissionsAPITestCases( - ViewSetBase, - ContactPermissionsAPIInheritedCases, -): - - pass - - - -class EmployeePermissionsAPIInheritedCases( - PermissionsAPITestCases, -): - - add_data: dict = None - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - - @classmethod - def setUpTestData(self): - - self.add_data.update( - super().add_data - ) - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) - - super().setUpTestData() - - - -class EmployeePermissionsAPITest( - PermissionsAPITestCases, - TestCase, -): - - pass - - - class ViewSetTestCases( - ViewSetBase, ContactViewSetInheritedCases, ): - pass + add_data: dict = { + 'employee_number': 123, + } + + kwargs_create_item: dict = { + 'employee_number': 456, + } + + kwargs_create_item_diff_org: dict = { + 'employee_number': 789, + } + + model = Employee @@ -100,21 +35,19 @@ class EmployeeViewSetInheritedCases( model = None - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - @classmethod def setUpTestData(self): - self.kwargs_create_item.update( - super().kwargs_create_item - ) + self.kwargs_create_item = { + **super().kwargs_create_item, + **self.kwargs_create_item + } - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) + self.kwargs_create_item_diff_org = { + **super().kwargs_create_item_diff_org, + **self.kwargs_create_item_diff_org + } super().setUpTestData() @@ -124,50 +57,4 @@ class EmployeeViewSetTest( ViewSetTestCases, TestCase, ): - - pass - - - -class MetadataTestCases( - ViewSetBase, - ContactMetadataInheritedCases, -): - - pass - - - -class EmployeeMetadataInheritedCases( - MetadataTestCases, -): - - model = None - - kwargs_create_item: dict = None - - kwargs_create_item_diff_org: dict = None - - - @classmethod - def setUpTestData(self): - - self.kwargs_create_item.update( - super().kwargs_create_item - ) - - self.kwargs_create_item_diff_org.update( - super().kwargs_create_item_diff_org - ) - - super().setUpTestData() - - - -class EmployeeMetadataTest( - MetadataTestCases, - TestCase, - -): - pass From a0ab0deb2aaf7bbbd16034560dfbfc1b8992d36e Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 16 May 2025 19:29:02 +0930 Subject: [PATCH 175/175] chore: squash migrations to reduce amount ref: #740 --- .../0007_rename_organization_tenant.py | 5 +- ..._entity_type_alter_person_dob_and_more.py} | 17 +++++- ...y_entity_type_alter_person_dob_and_more.py | 28 --------- app/accounting/migrations/0001_initial.py | 35 ++++++++++- .../migrations/0002_assetbasehistory.py | 29 ---------- .../migrations/0003_assetbasenotes.py | 29 ---------- .../0004_alter_assetbase_organization.py | 21 ------- .../0023_alter_ticketlinkeditem_item_type.py | 18 ------ ...ter_manufacturer_organization_and_more.py} | 58 ++++++++++++++++++- ...024_alter_ticketbase_opened_by_and_more.py | 47 --------------- .../migrations/0025_ticketcommentaction.py | 28 --------- .../0027_alter_ticketlinkeditem_item_type.py | 18 ------ app/itam/migrations/0011_itamassetbase.py | 31 ---------- ...ase_alter_device_organization_and_more.py} | 23 +++++++- 14 files changed, 125 insertions(+), 262 deletions(-) rename app/access/migrations/{0010_company.py => 0010_company_alter_entity_entity_type_alter_person_dob_and_more.py} (55%) delete mode 100644 app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py delete mode 100644 app/accounting/migrations/0002_assetbasehistory.py delete mode 100644 app/accounting/migrations/0003_assetbasenotes.py delete mode 100644 app/accounting/migrations/0004_alter_assetbase_organization.py delete mode 100644 app/core/migrations/0023_alter_ticketlinkeditem_item_type.py rename app/core/migrations/{0026_alter_manufacturer_organization_and_more.py => 0023_ticketcommentaction_alter_manufacturer_organization_and_more.py} (53%) delete mode 100644 app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py delete mode 100644 app/core/migrations/0025_ticketcommentaction.py delete mode 100644 app/core/migrations/0027_alter_ticketlinkeditem_item_type.py delete mode 100644 app/itam/migrations/0011_itamassetbase.py rename app/itam/migrations/{0012_alter_device_organization_and_more.py => 0011_itamassetbase_alter_device_organization_and_more.py} (77%) diff --git a/app/access/migrations/0007_rename_organization_tenant.py b/app/access/migrations/0007_rename_organization_tenant.py index 284afd96..aba76fb8 100644 --- a/app/access/migrations/0007_rename_organization_tenant.py +++ b/app/access/migrations/0007_rename_organization_tenant.py @@ -7,12 +7,11 @@ class Migration(migrations.Migration): dependencies = [ ('access', '0005_entity_person_entityhistory_entitynotes_role_and_more'), - ('accounting', '0003_assetbasenotes'), ('assistance', '0005_knowledgebasecategoryhistory_knowledgebasehistory'), ('config_management', '0007_configgroupshistory_configgrouphostshistory_and_more'), - ('core', '0025_ticketcommentaction'), + ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), ('devops', '0011_alter_gitgroup_unique_together_and_more'), - ('itam', '0011_itamassetbase'), + ('itam', '0010_alter_software_organization'), ('itim', '0009_slmticket_requestticket'), ('project_management', '0005_projecthistory_projectmilestonehistory_and_more'), ('settings', '0011_appsettingshistory_externallinkhistory'), diff --git a/app/access/migrations/0010_company.py b/app/access/migrations/0010_company_alter_entity_entity_type_alter_person_dob_and_more.py similarity index 55% rename from app/access/migrations/0010_company.py rename to app/access/migrations/0010_company_alter_entity_entity_type_alter_person_dob_and_more.py index 5733a568..377494e6 100644 --- a/app/access/migrations/0010_company.py +++ b/app/access/migrations/0010_company_alter_entity_entity_type_alter_person_dob_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.9 on 2025-05-15 07:47 +# Generated by Django 5.1.9 on 2025-05-16 09:58 import django.db.models.deletion from django.db import migrations, models @@ -25,4 +25,19 @@ class Migration(migrations.Migration): }, bases=('access.entity',), ), + migrations.AlterField( + model_name='entity', + name='entity_type', + field=models.CharField(help_text='Type this entity is', max_length=30, verbose_name='Entity Type'), + ), + migrations.AlterField( + model_name='person', + name='dob', + field=models.DateField(blank=True, help_text='The Persons Date of Birth (DOB)', null=True, verbose_name='DOB'), + ), + migrations.AlterField( + model_name='person', + name='m_name', + field=models.CharField(blank=True, help_text='The persons middle name(s)', max_length=100, null=True, verbose_name='Middle Name(s)'), + ), ] diff --git a/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py b/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py deleted file mode 100644 index b9c25071..00000000 --- a/app/access/migrations/0011_alter_entity_entity_type_alter_person_dob_and_more.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.9 on 2025-05-15 12:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0010_company'), - ] - - operations = [ - migrations.AlterField( - model_name='entity', - name='entity_type', - field=models.CharField(help_text='Type this entity is', max_length=30, verbose_name='Entity Type'), - ), - migrations.AlterField( - model_name='person', - name='dob', - field=models.DateField(blank=True, help_text='The Persons Date of Birth (DOB)', null=True, verbose_name='DOB'), - ), - migrations.AlterField( - model_name='person', - name='m_name', - field=models.CharField(blank=True, help_text='The persons middle name(s)', max_length=100, null=True, verbose_name='Middle Name(s)'), - ), - ] diff --git a/app/accounting/migrations/0001_initial.py b/app/accounting/migrations/0001_initial.py index 0e66e139..510202ad 100644 --- a/app/accounting/migrations/0001_initial.py +++ b/app/accounting/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.8 on 2025-05-04 12:42 +# Generated by Django 5.1.9 on 2025-05-16 09:58 import access.fields import access.models.tenancy @@ -13,7 +13,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('access', '0005_entity_person_entityhistory_entitynotes_role_and_more'), + ('access', '0010_company_alter_entity_entity_type_alter_person_dob_and_more'), + ('core', '0023_ticketcommentaction_alter_manufacturer_organization_and_more'), ] operations = [ @@ -27,7 +28,7 @@ class Migration(migrations.Migration): ('asset_type', models.CharField(blank=True, choices=accounting.models.asset_base.AssetBase.get_model_type_choices, default='asset', help_text='Asset Type. (derived from asset model)', max_length=30, validators=[accounting.models.asset_base.AssetBase.validate_not_null], verbose_name='Asset Type')), ('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of creation', verbose_name='Created')), ('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of last modification', verbose_name='Modified')), - ('organization', models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Organization')), + ('organization', models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant')), ], options={ 'verbose_name': 'Asset', @@ -36,4 +37,32 @@ class Migration(migrations.Migration): 'sub_model_type': 'asset', }, ), + migrations.CreateModel( + name='AssetBaseHistory', + fields=[ + ('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')), + ('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='accounting.assetbase', verbose_name='Model')), + ], + options={ + 'verbose_name': 'Asset History', + 'verbose_name_plural': 'Asset History', + 'db_table': 'accounting_assetbase_history', + 'ordering': ['-created'], + }, + bases=('core.modelhistory',), + ), + migrations.CreateModel( + name='AssetBaseNotes', + fields=[ + ('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')), + ('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='accounting.assetbase', verbose_name='Model')), + ], + options={ + 'verbose_name': 'Asset Note', + 'verbose_name_plural': 'Asset Notes', + 'db_table': 'accounting_assetbase_notes', + 'ordering': ['-created'], + }, + bases=('core.modelnotes',), + ), ] diff --git a/app/accounting/migrations/0002_assetbasehistory.py b/app/accounting/migrations/0002_assetbasehistory.py deleted file mode 100644 index d20db66c..00000000 --- a/app/accounting/migrations/0002_assetbasehistory.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.1.8 on 2025-05-05 06:29 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounting', '0001_initial'), - ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), - ] - - operations = [ - migrations.CreateModel( - name='AssetBaseHistory', - fields=[ - ('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')), - ('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='accounting.assetbase', verbose_name='Model')), - ], - options={ - 'verbose_name': 'Asset History', - 'verbose_name_plural': 'Asset History', - 'db_table': 'accounting_assetbase_history', - 'ordering': ['-created'], - }, - bases=('core.modelhistory',), - ), - ] diff --git a/app/accounting/migrations/0003_assetbasenotes.py b/app/accounting/migrations/0003_assetbasenotes.py deleted file mode 100644 index 46f7ca3e..00000000 --- a/app/accounting/migrations/0003_assetbasenotes.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.1.8 on 2025-05-05 06:41 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounting', '0002_assetbasehistory'), - ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), - ] - - operations = [ - migrations.CreateModel( - name='AssetBaseNotes', - fields=[ - ('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')), - ('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='accounting.assetbase', verbose_name='Model')), - ], - options={ - 'verbose_name': 'Asset Note', - 'verbose_name_plural': 'Asset Notes', - 'db_table': 'accounting_assetbase_notes', - 'ordering': ['-created'], - }, - bases=('core.modelnotes',), - ), - ] diff --git a/app/accounting/migrations/0004_alter_assetbase_organization.py b/app/accounting/migrations/0004_alter_assetbase_organization.py deleted file mode 100644 index 9c9154bd..00000000 --- a/app/accounting/migrations/0004_alter_assetbase_organization.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 5.1.9 on 2025-05-14 13:48 - -import access.models.tenancy -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), - ('accounting', '0003_assetbasenotes'), - ] - - operations = [ - migrations.AlterField( - model_name='assetbase', - name='organization', - field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), - ), - ] diff --git a/app/core/migrations/0023_alter_ticketlinkeditem_item_type.py b/app/core/migrations/0023_alter_ticketlinkeditem_item_type.py deleted file mode 100644 index 72ab01f8..00000000 --- a/app/core/migrations/0023_alter_ticketlinkeditem_item_type.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.8 on 2025-05-05 16:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='ticketlinkeditem', - name='item_type', - field=models.IntegerField(choices=[(1, 'Cluster'), (2, 'Config Group'), (3, 'Device'), (4, 'Operating System'), (5, 'Service'), (6, 'Software'), (7, 'Knowledge Base'), (8, 'Organization'), (9, 'Team'), (10, 'Feature Flag'), (11, 'Software Version'), (12, 'Ticket Category'), (13, 'Ticket Comment Category'), (14, 'Project State'), (15, 'Git Repository'), (16, 'Entity'), (17, 'Role'), (18, 'Asset'), (19, 'IT Asset')], help_text='Python Model location for linked item', verbose_name='Item Type'), - ), - ] diff --git a/app/core/migrations/0026_alter_manufacturer_organization_and_more.py b/app/core/migrations/0023_ticketcommentaction_alter_manufacturer_organization_and_more.py similarity index 53% rename from app/core/migrations/0026_alter_manufacturer_organization_and_more.py rename to app/core/migrations/0023_ticketcommentaction_alter_manufacturer_organization_and_more.py index d808a7e2..11e31904 100644 --- a/app/core/migrations/0026_alter_manufacturer_organization_and_more.py +++ b/app/core/migrations/0023_ticketcommentaction_alter_manufacturer_organization_and_more.py @@ -1,18 +1,35 @@ -# Generated by Django 5.1.9 on 2025-05-14 13:48 +# Generated by Django 5.1.9 on 2025-05-16 09:58 import access.models.tenancy +import core.models.ticket_comment_base import django.db.models.deletion +from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), - ('core', '0025_ticketcommentaction'), + ('access', '0010_company_alter_entity_entity_type_alter_person_dob_and_more'), + ('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ + migrations.CreateModel( + name='TicketCommentAction', + fields=[ + ('ticketcommentbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.ticketcommentbase')), + ], + options={ + 'verbose_name': 'Ticket Comment Action', + 'verbose_name_plural': 'Ticket Comment Actions', + 'ordering': ['id'], + 'permissions': [('import_ticketcommentaction', 'Can import ticket action comment.')], + 'sub_model_type': 'action', + }, + bases=('core.ticketcommentbase',), + ), migrations.AlterField( model_name='manufacturer', name='organization', @@ -43,6 +60,11 @@ class Migration(migrations.Migration): name='organization', field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), ), + migrations.AlterField( + model_name='ticketbase', + name='opened_by', + field=models.ForeignKey(blank=True, help_text='Who is the ticket for', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ticket_opened', to=settings.AUTH_USER_MODEL, verbose_name='Opened By'), + ), migrations.AlterField( model_name='ticketbase', name='organization', @@ -58,16 +80,46 @@ class Migration(migrations.Migration): name='organization', field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), ), + migrations.AlterField( + model_name='ticketcommentbase', + name='category', + field=models.ForeignKey(blank=True, help_text='Category of the comment', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.ticketcommentcategory', verbose_name='Category'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='comment_type', + field=models.CharField(choices=core.models.ticket_comment_base.TicketCommentBase.get_comment_type_choices, help_text='Type this comment is. derived from Meta.verbose_name', max_length=30, validators=[core.models.ticket_comment_base.TicketCommentBase.field_validation_not_empty], verbose_name='Type'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='external_ref', + field=models.IntegerField(blank=True, help_text='External System reference', null=True, verbose_name='Reference Number'), + ), + migrations.AlterField( + model_name='ticketcommentbase', + name='external_system', + field=models.IntegerField(blank=True, choices=[(1, 'Github'), (2, 'Gitlab'), (9999, 'Custom #1 (Imported)'), (9998, 'Custom #2 (Imported)'), (9997, 'Custom #3 (Imported)'), (9996, 'Custom #4 (Imported)'), (9995, 'Custom #5 (Imported)'), (9994, 'Custom #6 (Imported)'), (9993, 'Custom #7 (Imported)'), (9992, 'Custom #8 (Imported)'), (9991, 'Custom #9 (Imported)')], help_text='External system this item derives', null=True, verbose_name='External System'), + ), migrations.AlterField( model_name='ticketcommentbase', name='organization', field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), ), + migrations.AlterField( + model_name='ticketcommentbase', + name='parent', + field=models.ForeignKey(blank=True, help_text='Parent ID for creating discussion threads', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.ticketcommentbase', verbose_name='Parent Comment'), + ), migrations.AlterField( model_name='ticketcommentcategory', name='organization', field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'), ), + migrations.AlterField( + model_name='ticketlinkeditem', + name='item_type', + field=models.IntegerField(choices=[(1, 'Cluster'), (2, 'Config Group'), (3, 'Device'), (4, 'Operating System'), (5, 'Service'), (6, 'Software'), (7, 'Knowledge Base'), (8, 'Tenant'), (9, 'Team'), (10, 'Feature Flag'), (11, 'Software Version'), (12, 'Ticket Category'), (13, 'Ticket Comment Category'), (14, 'Project State'), (15, 'Git Repository'), (16, 'Entity'), (17, 'Role'), (18, 'Asset'), (19, 'IT Asset')], help_text='Python Model location for linked item', verbose_name='Item Type'), + ), migrations.AlterField( model_name='ticketlinkeditem', name='organization', diff --git a/app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py b/app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py deleted file mode 100644 index 18bffbfe..00000000 --- a/app/core/migrations/0024_alter_ticketbase_opened_by_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 5.1.9 on 2025-05-11 16:49 - -import core.models.ticket_comment_base -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0023_alter_ticketlinkeditem_item_type'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterField( - model_name='ticketbase', - name='opened_by', - field=models.ForeignKey(blank=True, help_text='Who is the ticket for', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='ticket_opened', to=settings.AUTH_USER_MODEL, verbose_name='Opened By'), - ), - migrations.AlterField( - model_name='ticketcommentbase', - name='category', - field=models.ForeignKey(blank=True, help_text='Category of the comment', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.ticketcommentcategory', verbose_name='Category'), - ), - migrations.AlterField( - model_name='ticketcommentbase', - name='comment_type', - field=models.CharField(choices=core.models.ticket_comment_base.TicketCommentBase.get_comment_type_choices, help_text='Type this comment is. derived from Meta.verbose_name', max_length=30, validators=[core.models.ticket_comment_base.TicketCommentBase.field_validation_not_empty], verbose_name='Type'), - ), - migrations.AlterField( - model_name='ticketcommentbase', - name='external_ref', - field=models.IntegerField(blank=True, help_text='External System reference', null=True, verbose_name='Reference Number'), - ), - migrations.AlterField( - model_name='ticketcommentbase', - name='external_system', - field=models.IntegerField(blank=True, choices=[(1, 'Github'), (2, 'Gitlab'), (9999, 'Custom #1 (Imported)'), (9998, 'Custom #2 (Imported)'), (9997, 'Custom #3 (Imported)'), (9996, 'Custom #4 (Imported)'), (9995, 'Custom #5 (Imported)'), (9994, 'Custom #6 (Imported)'), (9993, 'Custom #7 (Imported)'), (9992, 'Custom #8 (Imported)'), (9991, 'Custom #9 (Imported)')], help_text='External system this item derives', null=True, verbose_name='External System'), - ), - migrations.AlterField( - model_name='ticketcommentbase', - name='parent', - field=models.ForeignKey(blank=True, help_text='Parent ID for creating discussion threads', null=True, on_delete=django.db.models.deletion.PROTECT, to='core.ticketcommentbase', verbose_name='Parent Comment'), - ), - ] diff --git a/app/core/migrations/0025_ticketcommentaction.py b/app/core/migrations/0025_ticketcommentaction.py deleted file mode 100644 index b1360e3f..00000000 --- a/app/core/migrations/0025_ticketcommentaction.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.9 on 2025-05-13 09:48 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0024_alter_ticketbase_opened_by_and_more'), - ] - - operations = [ - migrations.CreateModel( - name='TicketCommentAction', - fields=[ - ('ticketcommentbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.ticketcommentbase')), - ], - options={ - 'verbose_name': 'Ticket Comment Action', - 'verbose_name_plural': 'Ticket Comment Actions', - 'ordering': ['id'], - 'permissions': [('import_ticketcommentaction', 'Can import ticket action comment.')], - 'sub_model_type': 'action', - }, - bases=('core.ticketcommentbase',), - ), - ] diff --git a/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py b/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py deleted file mode 100644 index 903cd524..00000000 --- a/app/core/migrations/0027_alter_ticketlinkeditem_item_type.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.9 on 2025-05-15 07:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0026_alter_manufacturer_organization_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='ticketlinkeditem', - name='item_type', - field=models.IntegerField(choices=[(1, 'Cluster'), (2, 'Config Group'), (3, 'Device'), (4, 'Operating System'), (5, 'Service'), (6, 'Software'), (7, 'Knowledge Base'), (8, 'Tenant'), (9, 'Team'), (10, 'Feature Flag'), (11, 'Software Version'), (12, 'Ticket Category'), (13, 'Ticket Comment Category'), (14, 'Project State'), (15, 'Git Repository'), (16, 'Entity'), (17, 'Role'), (18, 'Asset'), (19, 'IT Asset')], help_text='Python Model location for linked item', verbose_name='Item Type'), - ), - ] diff --git a/app/itam/migrations/0011_itamassetbase.py b/app/itam/migrations/0011_itamassetbase.py deleted file mode 100644 index 7373fcc2..00000000 --- a/app/itam/migrations/0011_itamassetbase.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 5.1.8 on 2025-05-04 13:58 - -import django.db.models.deletion -import itam.models.itam_asset_base -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounting', '0001_initial'), - ('itam', '0010_alter_software_organization'), - ] - - operations = [ - migrations.CreateModel( - name='ITAMAssetBase', - fields=[ - ('assetbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='accounting.assetbase')), - ('itam_type', models.CharField(blank=True, choices=itam.models.itam_asset_base.ITAMAssetBase.get_itam_model_type_choices, default='itam_base', help_text='IT Asset Type. (derived from IT asset model)', max_length=30, verbose_name='IT Asset Type')), - ], - options={ - 'verbose_name': 'IT Asset', - 'verbose_name_plural': 'IT Assets', - 'ordering': ['id'], - 'sub_model_type': 'it_asset', - 'itam_sub_model_type': 'itam_base', - }, - bases=('accounting.assetbase',), - ), - ] diff --git a/app/itam/migrations/0012_alter_device_organization_and_more.py b/app/itam/migrations/0011_itamassetbase_alter_device_organization_and_more.py similarity index 77% rename from app/itam/migrations/0012_alter_device_organization_and_more.py rename to app/itam/migrations/0011_itamassetbase_alter_device_organization_and_more.py index 24050cfd..4fd7c7d4 100644 --- a/app/itam/migrations/0012_alter_device_organization_and_more.py +++ b/app/itam/migrations/0011_itamassetbase_alter_device_organization_and_more.py @@ -1,18 +1,35 @@ -# Generated by Django 5.1.9 on 2025-05-14 13:48 +# Generated by Django 5.1.9 on 2025-05-16 09:58 import access.models.tenancy import django.db.models.deletion +import itam.models.itam_asset_base from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('access', '0008_alter_tenant_options_alter_entity_organization_and_more'), - ('itam', '0011_itamassetbase'), + ('access', '0010_company_alter_entity_entity_type_alter_person_dob_and_more'), + ('accounting', '0001_initial'), + ('itam', '0010_alter_software_organization'), ] operations = [ + migrations.CreateModel( + name='ITAMAssetBase', + fields=[ + ('assetbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='accounting.assetbase')), + ('itam_type', models.CharField(blank=True, choices=itam.models.itam_asset_base.ITAMAssetBase.get_itam_model_type_choices, default='itam_base', help_text='IT Asset Type. (derived from IT asset model)', max_length=30, verbose_name='IT Asset Type')), + ], + options={ + 'verbose_name': 'IT Asset', + 'verbose_name_plural': 'IT Assets', + 'ordering': ['id'], + 'sub_model_type': 'it_asset', + 'itam_sub_model_type': 'itam_base', + }, + bases=('accounting.assetbase',), + ), migrations.AlterField( model_name='device', name='organization',