Merge pull request #862 from nofusscomputing/refactor-switch-model-inheritence
This commit is contained in:
@ -0,0 +1,56 @@
|
||||
# Generated by Django 5.1.10 on 2025-07-12 07:20
|
||||
|
||||
import access.models.tenancy_abstract
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("access", "0019_companyaudithistory_companycenturionmodelnote"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="role",
|
||||
name="id",
|
||||
field=models.AutoField(
|
||||
help_text="ID of the item",
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
unique=True,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="role",
|
||||
name="model_notes",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="Tid bits of information",
|
||||
null=True,
|
||||
verbose_name="Notes",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="role",
|
||||
name="organization",
|
||||
field=models.ForeignKey(
|
||||
help_text="Tenant this belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="access.tenant",
|
||||
validators=[
|
||||
access.models.tenancy_abstract.TenancyAbstractModel.validatate_organization_exists
|
||||
],
|
||||
verbose_name="Tenant",
|
||||
),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="RoleHistory",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="RoleNotes",
|
||||
),
|
||||
]
|
@ -0,0 +1,81 @@
|
||||
# Generated by Django 5.1.10 on 2025-07-12 08:50
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("access", "0020_remove_rolenotes_model_and_more"),
|
||||
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="RoleAuditHistory",
|
||||
fields=[
|
||||
(
|
||||
"centurionaudit_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="core.centurionaudit",
|
||||
),
|
||||
),
|
||||
(
|
||||
"model",
|
||||
models.ForeignKey(
|
||||
help_text="Model this history belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="audit_history",
|
||||
to="access.role",
|
||||
verbose_name="Model",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Role History",
|
||||
"verbose_name_plural": "Role Histories",
|
||||
"db_table": "access_role_audithistory",
|
||||
"managed": True,
|
||||
},
|
||||
bases=("core.centurionaudit",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="RoleCenturionModelNote",
|
||||
fields=[
|
||||
(
|
||||
"centurionmodelnote_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="core.centurionmodelnote",
|
||||
),
|
||||
),
|
||||
(
|
||||
"model",
|
||||
models.ForeignKey(
|
||||
help_text="Model this note belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="access.role",
|
||||
verbose_name="Model",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Role Note",
|
||||
"verbose_name_plural": "Role Notes",
|
||||
"db_table": "access_role_centurionmodelnote",
|
||||
"managed": True,
|
||||
},
|
||||
bases=("core.centurionmodelnote",),
|
||||
),
|
||||
]
|
@ -1,5 +1,3 @@
|
||||
from .organization_history import OrganizationHistory # pylint: disable=W0611:unused-import
|
||||
from .role_history import RoleHistory # pylint: disable=W0611:unused-import
|
||||
|
||||
from .organization_notes import OrganizationNotes # pylint: disable=W0611:unused-import
|
||||
from .role_notes import RoleNotes # pylint: disable=W0611:unused-import
|
||||
|
@ -1,15 +1,20 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db import models
|
||||
|
||||
from access.fields import AutoCreatedField, AutoLastModifiedField
|
||||
from access.models.tenancy import TenancyObject
|
||||
from access.fields import AutoLastModifiedField
|
||||
|
||||
from core.models.centurion import CenturionModel
|
||||
|
||||
|
||||
|
||||
class Role(
|
||||
TenancyObject
|
||||
CenturionModel
|
||||
):
|
||||
|
||||
documentation = ''
|
||||
|
||||
model_tag = 'role'
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
@ -28,14 +33,6 @@ class Role(
|
||||
verbose_name_plural = 'Roles'
|
||||
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'Primary key of the entry',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
help_text = 'Name of this role',
|
||||
@ -53,12 +50,8 @@ class Role(
|
||||
verbose_name = 'Permissions'
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
is_global = None
|
||||
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
@ -66,8 +59,6 @@ class Role(
|
||||
return str( self.organization ) + ' / ' + self.name
|
||||
|
||||
|
||||
documentation = ''
|
||||
|
||||
page_layout: dict = [
|
||||
{
|
||||
"name": "Details",
|
||||
@ -156,19 +147,3 @@ class Role(
|
||||
return self._permissions_int
|
||||
|
||||
return self._permissions_int
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def save_history(self, before: dict, after: dict) -> bool:
|
||||
|
||||
from access.models.role_history import RoleHistory
|
||||
|
||||
history = super().save_history(
|
||||
before = before,
|
||||
after = after,
|
||||
history_model = RoleHistory
|
||||
)
|
||||
|
||||
return history
|
||||
|
@ -1,53 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
from core.models.model_history import ModelHistory
|
||||
|
||||
from access.models.role import Role
|
||||
|
||||
|
||||
|
||||
class RoleHistory(
|
||||
ModelHistory
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
db_table = 'access_role_history'
|
||||
|
||||
ordering = ModelHistory._meta.ordering
|
||||
|
||||
verbose_name = 'Role History'
|
||||
|
||||
verbose_name_plural = 'Role History'
|
||||
|
||||
|
||||
model = models.ForeignKey(
|
||||
Role,
|
||||
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 access.serializers.role import BaseSerializer
|
||||
|
||||
model = BaseSerializer(self.model, context = serializer_context)
|
||||
|
||||
return model
|
@ -1,45 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
from access.models.role import Role
|
||||
|
||||
from core.models.model_notes import ModelNotes
|
||||
|
||||
|
||||
|
||||
class RoleNotes(
|
||||
ModelNotes
|
||||
):
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
db_table = 'access_role_notes'
|
||||
|
||||
ordering = ModelNotes._meta.ordering
|
||||
|
||||
verbose_name = 'Role Note'
|
||||
|
||||
verbose_name_plural = 'Role Notes'
|
||||
|
||||
|
||||
model = models.ForeignKey(
|
||||
Role,
|
||||
blank = False,
|
||||
help_text = 'Model this note belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
related_name = 'notes',
|
||||
verbose_name = 'Model',
|
||||
)
|
||||
|
||||
table_fields: list = []
|
||||
|
||||
page_layout: dict = []
|
||||
|
||||
|
||||
def get_url_kwargs(self) -> dict:
|
||||
|
||||
return {
|
||||
'model_id': self.model.pk,
|
||||
'pk': self.pk
|
||||
}
|
56
app/access/serializers/centurionaudit_role.py
Normal file
56
app/access/serializers/centurionaudit_role.py
Normal file
@ -0,0 +1,56 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from drf_spectacular.utils import extend_schema_serializer
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from centurion.models.meta import RoleAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
|
||||
|
||||
from core.serializers.centurionaudit import (
|
||||
BaseSerializer,
|
||||
ViewSerializer as AuditHistoryViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'RoleAuditHistoryModelSerializer')
|
||||
class ModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
BaseSerializer
|
||||
):
|
||||
"""Git Group Audit History Base Model"""
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = RoleAuditHistory
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'display_name',
|
||||
'content_type',
|
||||
'model',
|
||||
'before',
|
||||
'after',
|
||||
'action',
|
||||
'user',
|
||||
'created',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'RoleAuditHistoryViewSerializer')
|
||||
class ViewSerializer(
|
||||
ModelSerializer,
|
||||
AuditHistoryViewSerializer,
|
||||
):
|
||||
"""Git Group Audit History Base View Model"""
|
||||
pass
|
87
app/access/serializers/centurionmodelnote_role.py
Normal file
87
app/access/serializers/centurionmodelnote_role.py
Normal file
@ -0,0 +1,87 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from drf_spectacular.utils import extend_schema_serializer
|
||||
|
||||
from access.serializers.organization import (TenantBaseSerializer)
|
||||
|
||||
from centurion.models.meta import RoleCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
|
||||
|
||||
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
|
||||
BaseSerializer,
|
||||
ModelSerializer as BaseModelModelSerializer,
|
||||
ViewSerializer as BaseModelViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'RoleModelNoteModelSerializer')
|
||||
class ModelSerializer(
|
||||
BaseModelModelSerializer,
|
||||
):
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
return {
|
||||
'_self': item.get_url( request = self._context['view'].request ),
|
||||
}
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = RoleCenturionModelNote
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'display_name',
|
||||
'body',
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'content_type',
|
||||
'model',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'organization',
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'content_type',
|
||||
'model',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
|
||||
def validate(self, attrs):
|
||||
|
||||
is_valid = False
|
||||
|
||||
note_model = self.Meta.model.model.field.related_model
|
||||
|
||||
attrs['model'] = note_model.objects.get(
|
||||
id = int( self.context['view'].kwargs['model_id'] )
|
||||
)
|
||||
|
||||
|
||||
is_valid = super().validate(attrs)
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'RoleModelNoteViewSerializer')
|
||||
class ViewSerializer(
|
||||
ModelSerializer,
|
||||
BaseModelViewSerializer,
|
||||
):
|
||||
|
||||
organization = TenantBaseSerializer( many = False, read_only = True )
|
@ -0,0 +1,44 @@
|
||||
import pytest
|
||||
|
||||
from django.test import Client
|
||||
|
||||
|
||||
class AdditionalTestCases:
|
||||
|
||||
|
||||
def test_permission_add(self, model_instance, api_request_permissions,
|
||||
model_kwargs, kwargs_api_create
|
||||
):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add as user with permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
|
||||
client.force_login( api_request_permissions['user']['add'] )
|
||||
|
||||
the_model = model_instance( kwargs_create = model_kwargs )
|
||||
|
||||
url = the_model.get_url( many = True )
|
||||
|
||||
response = client.post(
|
||||
path = url,
|
||||
data = kwargs_api_create,
|
||||
content_type = 'application/json'
|
||||
)
|
||||
|
||||
assert response.status_code == 200, response.content
|
||||
|
||||
|
||||
|
||||
def test_returned_data_from_user_and_global_organizations_only(
|
||||
self
|
||||
):
|
||||
"""Check items returned
|
||||
|
||||
Items returned from the query Must be from the users organization and
|
||||
global ONLY!
|
||||
"""
|
||||
|
||||
pytest.mark.xfail( reason = 'model is not for global use' )
|
@ -10,6 +10,8 @@ from access.serializers.role import Role, ModelSerializer
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_role
|
||||
@pytest.mark.module_role
|
||||
class ValidationSerializer(
|
||||
TestCase,
|
||||
):
|
||||
|
@ -1,4 +1,6 @@
|
||||
import django
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import Client, TestCase
|
||||
@ -20,6 +22,8 @@ User = django.contrib.auth.get_user_model()
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_role
|
||||
@pytest.mark.module_role
|
||||
class ViewSetBase:
|
||||
|
||||
add_data: dict = None
|
||||
@ -238,7 +242,7 @@ class RolePermissionsAPITest(
|
||||
|
||||
url_view_kwargs: dict = {}
|
||||
|
||||
url_name = '_api_v2_role'
|
||||
url_name = '_api_role'
|
||||
|
||||
|
||||
|
||||
@ -260,7 +264,7 @@ class RoleViewSetTest(
|
||||
|
||||
url_view_kwargs: dict = {}
|
||||
|
||||
url_name = '_api_v2_role'
|
||||
url_name = '_api_role'
|
||||
|
||||
|
||||
|
||||
@ -283,4 +287,4 @@ class RoleMetadataTest(
|
||||
|
||||
url_view_kwargs: dict = {}
|
||||
|
||||
url_name = '_api_v2_role'
|
||||
url_name = '_api_role'
|
||||
|
19
app/access/tests/unit/role/conftest.py
Normal file
19
app/access/tests/unit/role/conftest.py
Normal file
@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def model(model_role):
|
||||
|
||||
yield model_role
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class', autouse = True)
|
||||
def model_kwargs(request, kwargs_role):
|
||||
|
||||
request.cls.kwargs_create_item = kwargs_role.copy()
|
||||
|
||||
yield kwargs_role.copy()
|
||||
|
||||
if hasattr(request.cls, 'kwargs_create_item'):
|
||||
del request.cls.kwargs_create_item
|
@ -1,3 +1,4 @@
|
||||
import pytest
|
||||
import django
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
@ -18,6 +19,7 @@ User = django.contrib.auth.get_user_model()
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_role
|
||||
class APITestCases(
|
||||
APITenancyObject,
|
||||
):
|
||||
@ -154,6 +156,7 @@ class APITestCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_role
|
||||
class RoleAPITest(
|
||||
APITestCases,
|
||||
TestCase,
|
||||
@ -163,7 +166,7 @@ class RoleAPITest(
|
||||
|
||||
model = Role
|
||||
|
||||
url_ns_name = '_api_v2_role'
|
||||
url_ns_name = '_api_role'
|
||||
|
||||
|
||||
@classmethod
|
||||
|
@ -1,30 +1,70 @@
|
||||
from django.test import TestCase
|
||||
import pytest
|
||||
|
||||
from access.models.role import Role
|
||||
from django.db import models
|
||||
|
||||
from centurion.tests.unit.test_unit_models import (
|
||||
TenancyObjectInheritedCases
|
||||
|
||||
from core.tests.unit.centurion_abstract.test_unit_centurion_abstract_model import (
|
||||
CenturionAbstractModelInheritedCases
|
||||
)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_role
|
||||
class RoleModelTestCases(
|
||||
TenancyObjectInheritedCases,
|
||||
CenturionAbstractModelInheritedCases
|
||||
):
|
||||
|
||||
model = None
|
||||
|
||||
kwargs_item_create: dict = None
|
||||
@property
|
||||
def parameterized_class_attributes(self):
|
||||
|
||||
return {
|
||||
'model_tag': {
|
||||
'type': str,
|
||||
'value': 'role'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@property
|
||||
def parameterized_model_fields(self):
|
||||
|
||||
class RoleModelTest(
|
||||
RoleModelTestCases,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = Role
|
||||
|
||||
kwargs_item_create: dict = {
|
||||
'name': 'a role'
|
||||
return {
|
||||
'name': {
|
||||
'blank': False,
|
||||
'default': models.fields.NOT_PROVIDED,
|
||||
'field_type': models.CharField,
|
||||
'max_length': 30,
|
||||
'null': False,
|
||||
'unique': False,
|
||||
},
|
||||
'permissions': {
|
||||
'blank': True,
|
||||
'default': models.fields.NOT_PROVIDED,
|
||||
'field_type': models.ManyToManyField,
|
||||
'null': False,
|
||||
'unique': False,
|
||||
},
|
||||
'modified': {
|
||||
'blank': False,
|
||||
'default': models.fields.NOT_PROVIDED,
|
||||
'field_type': models.DateTimeField,
|
||||
'null': False,
|
||||
'unique': False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
class RoleModelInheritedCases(
|
||||
RoleModelTestCases,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_role
|
||||
class RoleModelPyTest(
|
||||
RoleModelTestCases,
|
||||
):
|
||||
pass
|
||||
|
@ -20,6 +20,8 @@ import pytest
|
||||
###############################################################################
|
||||
|
||||
|
||||
@pytest.mark.model_role
|
||||
@pytest.mark.module_role
|
||||
@pytest.mark.skip( reason = 'figure out how to isolate so entirety of unit tests can run without this test failing' )
|
||||
# @pytest.mark.forked
|
||||
# @pytest.mark.django_db
|
||||
|
@ -1,3 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
@ -9,6 +11,7 @@ from api.tests.unit.test_unit_common_viewset import ModelViewSetInheritedCases
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_role
|
||||
class ViewsetTestCases(
|
||||
ModelViewSetInheritedCases,
|
||||
):
|
||||
@ -44,6 +47,7 @@ class ViewsetTestCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_role
|
||||
class RoleViewsetTest(
|
||||
ViewsetTestCases,
|
||||
TestCase,
|
||||
@ -51,6 +55,6 @@ class RoleViewsetTest(
|
||||
|
||||
kwargs = {}
|
||||
|
||||
route_name = 'v2:_api_v2_role'
|
||||
route_name = 'v2:_api_role'
|
||||
|
||||
viewset = ViewSet
|
||||
|
@ -82,12 +82,7 @@ router.register(
|
||||
|
||||
router.register(
|
||||
prefix = 'role', viewset = role.ViewSet,
|
||||
feature_flag = '2025-00003', basename = '_api_v2_role'
|
||||
feature_flag = '2025-00003', basename = '_api_role'
|
||||
)
|
||||
|
||||
# router.register(
|
||||
# prefix = 'role/(?P<model_id>[0-9]+)/notes', viewset = role_notes.ViewSet,
|
||||
# feature_flag = '2025-00003', basename = '_api_v2_role_note'
|
||||
# )
|
||||
|
||||
urlpatterns = router.urls
|
||||
|
@ -37,7 +37,7 @@ class Index(IndexViewset):
|
||||
if self.request.feature_flag['2025-00003']:
|
||||
|
||||
response.update({
|
||||
"role": reverse( 'v2:_api_v2_role-list', request=request ),
|
||||
"role": reverse( 'v2:_api_role-list', request=request ),
|
||||
})
|
||||
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
|
||||
|
||||
# THis import only exists so that the migrations can be created
|
||||
from access.models.role_history import RoleHistory # pylint: disable=W0611:unused-import
|
||||
from access.serializers.role import (
|
||||
Role,
|
||||
ModelSerializer,
|
||||
|
@ -0,0 +1,118 @@
|
||||
# Generated by Django 5.1.10 on 2025-07-10 09:10
|
||||
|
||||
import access.models.tenancy_abstract
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("access", "0019_companyaudithistory_companycenturionmodelnote"),
|
||||
("accounting", "0001_initial"),
|
||||
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="assetbase",
|
||||
name="id",
|
||||
field=models.AutoField(
|
||||
help_text="ID of the item",
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
unique=True,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="assetbase",
|
||||
name="model_notes",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="Tid bits of information",
|
||||
null=True,
|
||||
verbose_name="Notes",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="assetbase",
|
||||
name="organization",
|
||||
field=models.ForeignKey(
|
||||
help_text="Tenant this belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="access.tenant",
|
||||
validators=[
|
||||
access.models.tenancy_abstract.TenancyAbstractModel.validatate_organization_exists
|
||||
],
|
||||
verbose_name="Tenant",
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="AssetBaseAuditHistory",
|
||||
fields=[
|
||||
(
|
||||
"centurionaudit_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="core.centurionaudit",
|
||||
),
|
||||
),
|
||||
(
|
||||
"model",
|
||||
models.ForeignKey(
|
||||
help_text="Model this history belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="audit_history",
|
||||
to="accounting.assetbase",
|
||||
verbose_name="Model",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Asset History",
|
||||
"verbose_name_plural": "Asset Histories",
|
||||
"db_table": "accounting_assetbase_audithistory",
|
||||
"managed": True,
|
||||
},
|
||||
bases=("core.centurionaudit",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="AssetBaseCenturionModelNote",
|
||||
fields=[
|
||||
(
|
||||
"centurionmodelnote_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="core.centurionmodelnote",
|
||||
),
|
||||
),
|
||||
(
|
||||
"model",
|
||||
models.ForeignKey(
|
||||
help_text="Model this note belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="accounting.assetbase",
|
||||
verbose_name="Model",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Asset Note",
|
||||
"verbose_name_plural": "Asset Notes",
|
||||
"db_table": "accounting_assetbase_centurionmodelnote",
|
||||
"managed": True,
|
||||
},
|
||||
bases=("core.centurionmodelnote",),
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.1.10 on 2025-07-10 09:31
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("accounting", "0002_alter_assetbase_id_alter_assetbase_model_notes_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name="AssetBaseHistory",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="AssetBaseNotes",
|
||||
),
|
||||
]
|
@ -1,5 +0,0 @@
|
||||
from .asset_base_history import AssetBaseHistory # pylint: disable=W0611:unused-import
|
||||
|
||||
from .asset_base_history import AssetBaseHistory # pylint: disable=W0611:unused-import
|
||||
|
||||
from .asset_base_notes import AssetBaseNotes # pylint: disable=W0611:unused-import
|
||||
|
@ -1,15 +1,14 @@
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
from access.fields import AutoLastModifiedField
|
||||
|
||||
from access.fields import AutoCreatedField, AutoLastModifiedField
|
||||
from access.models.tenancy import TenancyObject
|
||||
from core.models.centurion import CenturionModel
|
||||
|
||||
|
||||
|
||||
class AssetBase(
|
||||
TenancyObject,
|
||||
CenturionModel,
|
||||
):
|
||||
"""Asset Base Model
|
||||
|
||||
@ -21,6 +20,10 @@ class AssetBase(
|
||||
|
||||
app_namespace = 'accounting'
|
||||
|
||||
model_tag = 'asset'
|
||||
|
||||
url_model_name = 'asset'
|
||||
|
||||
|
||||
@property
|
||||
def _base_model(self):
|
||||
@ -50,18 +53,6 @@ class AssetBase(
|
||||
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',
|
||||
@ -96,14 +87,11 @@ class AssetBase(
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# Status
|
||||
|
||||
# model (manufacturer / model)
|
||||
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def get_model_type(self):
|
||||
"""Fetch the Ticket Type
|
||||
@ -137,7 +125,7 @@ class AssetBase(
|
||||
|
||||
if(
|
||||
( isinstance(model, AssetBase) or issubclass(model, AssetBase) )
|
||||
and AssetBase._meta.sub_model_type != 'asset'
|
||||
# and AssetBase._meta.sub_model_type != 'asset'
|
||||
|
||||
):
|
||||
|
||||
@ -159,12 +147,6 @@ class AssetBase(
|
||||
verbose_name = 'Asset Type',
|
||||
)
|
||||
|
||||
|
||||
|
||||
created = AutoCreatedField(
|
||||
editable = True,
|
||||
)
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
|
||||
@ -237,6 +219,26 @@ class AssetBase(
|
||||
|
||||
|
||||
|
||||
def clean_fields(self, exclude = 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(' ', '_')
|
||||
and str(related_model._meta.sub_model_type).lower().replace(' ', '_') != 'asset'
|
||||
):
|
||||
|
||||
self.asset_type = str(related_model._meta.sub_model_type).lower().replace(' ', '_')
|
||||
|
||||
|
||||
super().clean_fields(exclude = exclude)
|
||||
|
||||
|
||||
|
||||
def get_related_field_name(self) -> str:
|
||||
|
||||
meta = getattr(self, '_meta')
|
||||
@ -260,6 +262,7 @@ class AssetBase(
|
||||
return ''
|
||||
|
||||
|
||||
|
||||
def get_related_model(self):
|
||||
"""Recursive model Fetch
|
||||
|
||||
@ -292,74 +295,3 @@ class AssetBase(
|
||||
|
||||
|
||||
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 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)
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
@ -1,53 +0,0 @@
|
||||
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
|
@ -1,47 +0,0 @@
|
||||
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
|
||||
}
|
56
app/accounting/serializers/centurionaudit_assetbase.py
Normal file
56
app/accounting/serializers/centurionaudit_assetbase.py
Normal file
@ -0,0 +1,56 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from drf_spectacular.utils import extend_schema_serializer
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from centurion.models.meta import AssetBaseAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
|
||||
|
||||
from core.serializers.centurionaudit import (
|
||||
BaseSerializer,
|
||||
ViewSerializer as AuditHistoryViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'AssetBaseAuditHistoryModelSerializer')
|
||||
class ModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
BaseSerializer
|
||||
):
|
||||
"""Git Group Audit History Base Model"""
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = AssetBaseAuditHistory
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'display_name',
|
||||
'content_type',
|
||||
'model',
|
||||
'before',
|
||||
'after',
|
||||
'action',
|
||||
'user',
|
||||
'created',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'AssetBaseAuditHistoryViewSerializer')
|
||||
class ViewSerializer(
|
||||
ModelSerializer,
|
||||
AuditHistoryViewSerializer,
|
||||
):
|
||||
"""Git Group Audit History Base View Model"""
|
||||
pass
|
87
app/accounting/serializers/centurionmodelnote_assetbase.py
Normal file
87
app/accounting/serializers/centurionmodelnote_assetbase.py
Normal file
@ -0,0 +1,87 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from drf_spectacular.utils import extend_schema_serializer
|
||||
|
||||
from access.serializers.organization import (TenantBaseSerializer)
|
||||
|
||||
from centurion.models.meta import AssetBaseCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
|
||||
|
||||
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
|
||||
BaseSerializer,
|
||||
ModelSerializer as BaseModelModelSerializer,
|
||||
ViewSerializer as BaseModelViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'AssetBaseModelNoteModelSerializer')
|
||||
class ModelSerializer(
|
||||
BaseModelModelSerializer,
|
||||
):
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
return {
|
||||
'_self': item.get_url( request = self._context['view'].request ),
|
||||
}
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = AssetBaseCenturionModelNote
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'display_name',
|
||||
'body',
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'content_type',
|
||||
'model',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'organization',
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'content_type',
|
||||
'model',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
|
||||
def validate(self, attrs):
|
||||
|
||||
is_valid = False
|
||||
|
||||
note_model = self.Meta.model.model.field.related_model
|
||||
|
||||
attrs['model'] = note_model.objects.get(
|
||||
id = int( self.context['view'].kwargs['model_id'] )
|
||||
)
|
||||
|
||||
|
||||
is_valid = super().validate(attrs)
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'AssetBaseModelNoteViewSerializer')
|
||||
class ViewSerializer(
|
||||
ModelSerializer,
|
||||
BaseModelViewSerializer,
|
||||
):
|
||||
|
||||
organization = TenantBaseSerializer( many = False, read_only = True )
|
@ -18,7 +18,7 @@ def model(request):
|
||||
@pytest.fixture(scope='function')
|
||||
def create_serializer():
|
||||
|
||||
from accounting.serializers.asset import ModelSerializer
|
||||
from accounting.serializers.assetbase import ModelSerializer
|
||||
|
||||
|
||||
yield ModelSerializer
|
||||
|
@ -1,4 +1,6 @@
|
||||
import django
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
@ -15,6 +17,7 @@ User = django.contrib.auth.get_user_model()
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_assetbase
|
||||
class MetadataTestCases(
|
||||
MetadataAttributesFunctional,
|
||||
):
|
||||
@ -234,7 +237,7 @@ class AssetBaseMetadataInheritedCases(
|
||||
|
||||
kwargs_create_item_diff_org: dict = {}
|
||||
|
||||
url_name = 'accounting:_api_v2_asset_sub'
|
||||
url_name = 'accounting:_api_asset_sub'
|
||||
|
||||
|
||||
@classmethod
|
||||
@ -251,21 +254,22 @@ class AssetBaseMetadataInheritedCases(
|
||||
}
|
||||
|
||||
self.url_kwargs = {
|
||||
'asset_model': self.model._meta.sub_model_type
|
||||
'model_name': self.model._meta.model_name
|
||||
}
|
||||
|
||||
self.url_view_kwargs = {
|
||||
'asset_model': self.model._meta.sub_model_type
|
||||
'model_name': self.model._meta.model_name
|
||||
}
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class AssetBaseMetadataTest(
|
||||
MetadataTestCases,
|
||||
TestCase,
|
||||
|
||||
):
|
||||
|
||||
url_name = 'accounting:_api_v2_asset'
|
||||
url_name = 'accounting:_api_asset'
|
||||
|
@ -6,6 +6,7 @@ from api.tests.functional.test_functional_api_permissions import (
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_assetbase
|
||||
class PermissionsAPITestCases(
|
||||
APIPermissionsInheritedCases,
|
||||
):
|
||||
@ -36,7 +37,7 @@ class PermissionsAPITestCases(
|
||||
|
||||
url_kwargs: dict = {}
|
||||
|
||||
url_name = 'accounting:_api_v2_asset'
|
||||
url_name = 'accounting:_api_asset'
|
||||
|
||||
url_view_kwargs: dict = {}
|
||||
|
||||
@ -65,18 +66,18 @@ class AssetBasePermissionsAPIInheritedCases(
|
||||
|
||||
kwargs_create_item_diff_org: dict = None
|
||||
|
||||
url_name = 'accounting:_api_v2_asset_sub'
|
||||
url_name = 'accounting:_api_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
|
||||
'model_name': self.model._meta.model_name
|
||||
})
|
||||
|
||||
request.cls.url_view_kwargs.update({
|
||||
'asset_model': self.model._meta.sub_model_type
|
||||
'model_name': self.model._meta.model_name
|
||||
})
|
||||
|
||||
|
||||
@ -94,6 +95,8 @@ class AssetBasePermissionsAPIInheritedCases(
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class AssetBasePermissionsAPIPyTest(
|
||||
PermissionsAPITestCases,
|
||||
):
|
||||
|
@ -27,6 +27,7 @@ class MockView:
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_assetbase
|
||||
class AssetBaseSerializerTestCases:
|
||||
|
||||
|
||||
@ -188,6 +189,7 @@ class AssetBaseSerializerInheritedCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class AssetBaseSerializerPyTest(
|
||||
AssetBaseSerializerTestCases,
|
||||
):
|
||||
|
@ -1,4 +1,6 @@
|
||||
import django
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
@ -15,6 +17,7 @@ User = django.contrib.auth.get_user_model()
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_assetbase
|
||||
class ViewSetBase:
|
||||
|
||||
add_data: dict = {
|
||||
@ -237,7 +240,7 @@ class AssetBaseViewSetInheritedCases(
|
||||
|
||||
model = None
|
||||
|
||||
url_name = 'accounting:_api_v2_asset_sub'
|
||||
url_name = 'accounting:_api_asset_sub'
|
||||
|
||||
|
||||
@classmethod
|
||||
@ -254,20 +257,21 @@ class AssetBaseViewSetInheritedCases(
|
||||
}
|
||||
|
||||
self.url_kwargs = {
|
||||
'asset_model': self.model._meta.sub_model_type
|
||||
'model_name': self.model._meta.model_name
|
||||
}
|
||||
|
||||
self.url_view_kwargs = {
|
||||
'asset_model': self.model._meta.sub_model_type
|
||||
'model_name': self.model._meta.model_name
|
||||
}
|
||||
|
||||
super().setUpTestData()
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class AssetBaseViewSetTest(
|
||||
ViewSetTestCases,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
url_name = 'accounting:_api_v2_asset'
|
||||
url_name = 'accounting:_api_asset'
|
||||
|
@ -1,14 +1,22 @@
|
||||
import pytest
|
||||
|
||||
from accounting.models.asset_base import AssetBase
|
||||
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def model(request):
|
||||
def model(model_assetbase):
|
||||
|
||||
request.cls.model = AssetBase
|
||||
yield model_assetbase
|
||||
|
||||
yield request.cls.model
|
||||
|
||||
del request.cls.model
|
||||
@pytest.fixture( scope = 'class', autouse = True)
|
||||
def model_kwargs(request, kwargs_assetbase):
|
||||
|
||||
request.cls.kwargs_create_item = kwargs_assetbase.copy()
|
||||
|
||||
yield kwargs_assetbase.copy()
|
||||
|
||||
if hasattr(request.cls, 'kwargs_create_item'):
|
||||
try:
|
||||
del request.cls.kwargs_create_item
|
||||
except:
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from api.tests.functional.test_functional_api_fields import (
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_assetbase
|
||||
class AssetBaseAPITestCases(
|
||||
APIFieldsInheritedCases,
|
||||
):
|
||||
@ -23,7 +24,7 @@ class AssetBaseAPITestCases(
|
||||
if model != self.base_model:
|
||||
|
||||
request.cls.url_view_kwargs.update({
|
||||
'asset_model': model._meta.sub_model_type,
|
||||
'model_name': model._meta.model_name,
|
||||
})
|
||||
|
||||
|
||||
@ -55,7 +56,7 @@ class AssetBaseAPITestCases(
|
||||
'serial_number': '65756756756',
|
||||
}
|
||||
|
||||
url_ns_name = 'accounting:_api_v2_asset'
|
||||
url_ns_name = 'accounting:_api_asset'
|
||||
"""Url namespace (optional, if not required) and url name"""
|
||||
|
||||
|
||||
@ -68,10 +69,11 @@ class AssetBaseAPIInheritedCases(
|
||||
|
||||
model = None
|
||||
|
||||
url_ns_name = 'accounting:_api_v2_asset_sub'
|
||||
url_ns_name = 'accounting:_api_asset_sub'
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class AssetBaseAPIPyTest(
|
||||
AssetBaseAPITestCases,
|
||||
):
|
||||
|
@ -4,204 +4,96 @@ from django.db import models
|
||||
|
||||
from accounting.models.asset_base import AssetBase
|
||||
|
||||
from centurion.tests.unit.test_unit_models import (
|
||||
PyTestTenancyObjectInheritedCases,
|
||||
from core.tests.unit.centurion_abstract.test_unit_centurion_abstract_model import (
|
||||
CenturionAbstractModelInheritedCases
|
||||
)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_assetbase
|
||||
class AssetBaseModelTestCases(
|
||||
PyTestTenancyObjectInheritedCases,
|
||||
CenturionAbstractModelInheritedCases
|
||||
):
|
||||
|
||||
base_model = AssetBase
|
||||
|
||||
kwargs_create_item: dict = {
|
||||
'asset_number': 'a12s432',
|
||||
'serial_number': 'abbcccdddd',
|
||||
}
|
||||
@property
|
||||
def parameterized_class_attributes(self):
|
||||
|
||||
sub_model_type = 'asset'
|
||||
"""Sub Model Type
|
||||
|
||||
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
|
||||
"""
|
||||
|
||||
|
||||
parameterized_fields: dict = {
|
||||
"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,
|
||||
return {
|
||||
'app_namespace': {
|
||||
'type': str,
|
||||
'value': 'accounting'
|
||||
},
|
||||
'model_tag': {
|
||||
'type': str,
|
||||
'value': 'asset'
|
||||
},
|
||||
'url_model_name': {
|
||||
'type': str,
|
||||
'value': 'asset'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@property
|
||||
def parameterized_model_fields(self):
|
||||
|
||||
return {
|
||||
'asset_number': {
|
||||
'blank': True,
|
||||
'default': models.fields.NOT_PROVIDED,
|
||||
'field_type': models.CharField,
|
||||
'max_length': 30,
|
||||
'null': True,
|
||||
'unique': True,
|
||||
},
|
||||
'serial_number': {
|
||||
'blank': True,
|
||||
'default': models.fields.NOT_PROVIDED,
|
||||
'field_type': models.CharField,
|
||||
'max_length': 30,
|
||||
'null': True,
|
||||
'unique': True,
|
||||
},
|
||||
'asset_type': {
|
||||
'blank': True,
|
||||
'default': 'asset',
|
||||
'field_type': models.CharField,
|
||||
'max_length': 30,
|
||||
'null': False,
|
||||
'unique': False,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@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):
|
||||
def test_class_inherits_assetbase(self, model):
|
||||
""" 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'
|
||||
|
||||
|
||||
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)
|
||||
assert issubclass(model, AssetBase)
|
||||
|
||||
|
||||
|
||||
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
|
||||
def test_method_get_url_kwargs(self, mocker, model_instance, settings):
|
||||
|
||||
Ticket sub-models must have this attribute defined in `ModelNam.Meta.sub_model_type`
|
||||
"""
|
||||
url = model_instance.get_url_kwargs()
|
||||
|
||||
|
||||
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
|
||||
assert model_instance.get_url_kwargs() == {
|
||||
'model_name': model_instance._meta.model_name,
|
||||
'pk': model_instance.id
|
||||
}
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class AssetBaseModelPyTest(
|
||||
AssetBaseModelTestCases,
|
||||
):
|
||||
|
||||
|
||||
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
|
||||
pass
|
||||
|
@ -1,3 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
@ -11,8 +13,13 @@ from accounting.viewsets.asset import (
|
||||
|
||||
from api.tests.unit.test_unit_common_viewset import SubModelViewSetInheritedCases
|
||||
|
||||
from centurion.tests.abstract.mock_view import MockRequest
|
||||
|
||||
from settings.models.app_settings import AppSettings
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_assetbase
|
||||
class AssetBaseViewsetTestCases(
|
||||
SubModelViewSetInheritedCases,
|
||||
):
|
||||
@ -43,7 +50,7 @@ class AssetBaseViewsetTestCases(
|
||||
if self.model is not AssetBase:
|
||||
|
||||
self.kwargs = {
|
||||
'asset_model': self.model._meta.sub_model_type
|
||||
'model_name': self.model._meta.sub_model_type
|
||||
}
|
||||
|
||||
self.viewset.kwargs = self.kwargs
|
||||
@ -60,6 +67,8 @@ class AssetBaseViewsetTestCases(
|
||||
|
||||
self.http_options_response_list = client.options(url)
|
||||
|
||||
a = 'a'
|
||||
|
||||
|
||||
|
||||
def test_view_attr_value_model_kwarg(self):
|
||||
@ -70,7 +79,30 @@ class AssetBaseViewsetTestCases(
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.model_kwarg == 'asset_model'
|
||||
assert view_set.model_kwarg == 'model_name'
|
||||
|
||||
|
||||
|
||||
def test_view_attr_model_value(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `model` must return the correct sub-model
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
|
||||
app_settings = AppSettings.objects.select_related('global_organization').get(
|
||||
owner_organization = None
|
||||
)
|
||||
|
||||
|
||||
view_set.request = MockRequest(
|
||||
user = self.view_user,
|
||||
app_settings = app_settings,
|
||||
)
|
||||
|
||||
assert view_set.model == self.model
|
||||
|
||||
|
||||
|
||||
@ -85,10 +117,11 @@ class AssetBaseViewsetInheritedCases(
|
||||
model: str = None
|
||||
"""name of the model to test"""
|
||||
|
||||
route_name = 'v2:accounting:_api_v2_asset_sub'
|
||||
route_name = 'v2:accounting:_api_asset_sub'
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class AssetBaseViewsetTest(
|
||||
AssetBaseViewsetTestCases,
|
||||
TestCase,
|
||||
@ -96,6 +129,6 @@ class AssetBaseViewsetTest(
|
||||
|
||||
kwargs = {}
|
||||
|
||||
route_name = 'v2:accounting:_api_v2_asset'
|
||||
route_name = 'v2:accounting:_api_asset'
|
||||
|
||||
viewset = NoDocsViewSet
|
||||
|
@ -33,7 +33,7 @@ for model in apps.get_models():
|
||||
if model._meta.sub_model_type == 'asset':
|
||||
continue
|
||||
|
||||
asset_type_names += model._meta.sub_model_type + '|'
|
||||
asset_type_names += model._meta.model_name + '|'
|
||||
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ asset_type_names = str(asset_type_names)[:-1]
|
||||
if not asset_type_names:
|
||||
asset_type_names = 'none'
|
||||
|
||||
router.register(f'asset/(?P<asset_model>[{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')
|
||||
router.register(f'asset/(?P<model_name>[{asset_type_names}]+)?', asset.ViewSet, feature_flag = '2025-00004', basename='_api_asset_sub')
|
||||
router.register('asset', asset.NoDocsViewSet, feature_flag = '2025-00004', basename='_api_asset')
|
||||
|
||||
urlpatterns = router.urls
|
||||
|
@ -12,7 +12,7 @@ from drf_spectacular.utils import (
|
||||
|
||||
from accounting.models.asset_base import AssetBase
|
||||
|
||||
from api.viewsets.common import SubModelViewSet
|
||||
from api.viewsets.common import SubModelViewSet_ReWrite
|
||||
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ def spectacular_request_serializers( serializer_type = 'Model'):
|
||||
|
||||
if issubclass(model, AssetBase):
|
||||
|
||||
serializer_name = 'asset'
|
||||
serializer_name = 'assetbase'
|
||||
|
||||
if(
|
||||
model._meta.model_name == 'assetbase'
|
||||
@ -34,7 +34,7 @@ def spectacular_request_serializers( serializer_type = 'Model'):
|
||||
continue
|
||||
|
||||
|
||||
serializer_name += '_' + model._meta.sub_model_type
|
||||
serializer_name += '_' + model._meta.model_name
|
||||
|
||||
serializer_module = importlib.import_module(
|
||||
model._meta.app_label + '.serializers.' + str(
|
||||
@ -56,7 +56,7 @@ def spectacular_request_serializers( serializer_type = 'Model'):
|
||||
description='.',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'asset_model',
|
||||
name = 'model_name',
|
||||
description = 'Enter the asset type. This is the name of the asset sub-model.',
|
||||
location = OpenApiParameter.PATH,
|
||||
type = str,
|
||||
@ -97,7 +97,7 @@ def spectacular_request_serializers( serializer_type = 'Model'):
|
||||
description = '.',
|
||||
parameters =[
|
||||
OpenApiParameter(
|
||||
name = 'asset_model',
|
||||
name = 'model_name',
|
||||
description = 'Enter the asset type. This is the name of the asset sub-model.',
|
||||
location = OpenApiParameter.PATH,
|
||||
type = str,
|
||||
@ -121,7 +121,7 @@ def spectacular_request_serializers( serializer_type = 'Model'):
|
||||
description='.',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'asset_model',
|
||||
name = 'model_name',
|
||||
description = 'Enter the asset model. This is the name of the asset sub-model.',
|
||||
location = OpenApiParameter.PATH,
|
||||
type = str,
|
||||
@ -153,7 +153,7 @@ def spectacular_request_serializers( serializer_type = 'Model'):
|
||||
description='.',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'asset_model',
|
||||
name = 'model_name',
|
||||
description = 'Enter the asset model. This is the name of the Asset sub-model.',
|
||||
location = OpenApiParameter.PATH,
|
||||
type = str,
|
||||
@ -214,7 +214,7 @@ def spectacular_request_serializers( serializer_type = 'Model'):
|
||||
}
|
||||
),
|
||||
)
|
||||
class ViewSet( SubModelViewSet ):
|
||||
class ViewSet( SubModelViewSet_ReWrite ):
|
||||
|
||||
_has_purge: bool = False
|
||||
"""User Permission
|
||||
@ -232,7 +232,7 @@ class ViewSet( SubModelViewSet ):
|
||||
# 'is_deleted'
|
||||
]
|
||||
|
||||
model_kwarg = 'asset_model'
|
||||
model_kwarg = 'model_name'
|
||||
|
||||
search_fields = [
|
||||
'asset_number',
|
||||
|
@ -638,7 +638,7 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
|
||||
'view_itamassetbase': {
|
||||
"display_name": "IT Assets",
|
||||
"name": "itasset",
|
||||
"link": "/itam/it_asset"
|
||||
"link": "/itam/itamassetbase"
|
||||
},
|
||||
**nav['itam']['pages']
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ class APIFieldsTestCases:
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view" + str(random_str), password="password")
|
||||
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view" + str(random_str), password="password", is_superuser = True)
|
||||
|
||||
team_user = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
|
@ -8,7 +8,8 @@ from django.utils.module_loading import import_string
|
||||
# Note: Only included so that it can be picked up.
|
||||
# in future when model referenced, this include statement may be repoved.
|
||||
from access.models.company_base import Company # pylint: disable=W0611:unused-import
|
||||
|
||||
from access.models.role import Role # pylint: disable=W0611:unused-import
|
||||
## EoF Include block
|
||||
|
||||
|
||||
module_path = f'centurion.models.meta'
|
||||
|
@ -76,10 +76,7 @@ CREATE TABLE IF NOT EXISTS "assistance_knowledge_base_history" ("modelhistory_pt
|
||||
CREATE TABLE IF NOT EXISTS "access_team_history" ("modelhistory_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_model_history" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_team" ("group_ptr_id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "core_ticketcategory_notes" ("modelnotes_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_model_notes" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "core_ticketcategory" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "core_ticketcommentcategory_notes" ("modelnotes_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_model_notes" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "core_ticketcommentcategory" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_role" ("model_notes" text NULL, "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(30) NOT NULL, "created" datetime NOT NULL, "modified" datetime NOT NULL, "organization_id" integer NOT NULL REFERENCES "access_tenant" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_role_permissions" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "role_id" integer NOT NULL REFERENCES "access_role" ("id") DEFERRABLE INITIALLY DEFERRED, "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_role_history" ("modelhistory_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_model_history" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_role" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_role_notes" ("modelnotes_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_model_notes" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_role" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_contact" ("person_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "access_person" ("entity_ptr_id") DEFERRABLE INITIALLY DEFERRED, "directory" bool NOT NULL, "email" varchar(254) NOT NULL UNIQUE);
|
||||
CREATE TABLE IF NOT EXISTS "core_ticketbase_assigned_to" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "ticketbase_id" integer NOT NULL REFERENCES "core_ticketbase" ("id") DEFERRABLE INITIALLY DEFERRED, "entity_id" integer NOT NULL REFERENCES "access_entity" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "core_ticketbase_subscribed_to" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "ticketbase_id" integer NOT NULL REFERENCES "core_ticketbase" ("id") DEFERRABLE INITIALLY DEFERRED, "entity_id" integer NOT NULL REFERENCES "access_entity" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
@ -139,9 +136,12 @@ CREATE TABLE IF NOT EXISTS "access_person_audithistory" ("centurionaudit_ptr_id"
|
||||
CREATE TABLE IF NOT EXISTS "access_person_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_person" ("entity_ptr_id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_company_audithistory" ("centurionaudit_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_audithistory" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_company" ("entity_ptr_id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_company_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_company" ("entity_ptr_id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "accounting_assetbase" ("model_notes" text NULL, "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "asset_number" varchar(30) NULL UNIQUE, "serial_number" varchar(30) NULL UNIQUE, "asset_type" varchar(30) NOT NULL, "created" datetime NOT NULL, "modified" datetime NOT NULL, "organization_id" integer NOT NULL REFERENCES "access_tenant" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "accounting_assetbase_history" ("modelhistory_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_model_history" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "accounting_assetbase_notes" ("modelnotes_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_model_notes" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_role" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(30) NOT NULL, "created" datetime NOT NULL, "modified" datetime NOT NULL, "organization_id" integer NOT NULL REFERENCES "access_tenant" ("id") DEFERRABLE INITIALLY DEFERRED, "model_notes" text NULL);
|
||||
CREATE TABLE IF NOT EXISTS "access_role_audithistory" ("centurionaudit_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_audithistory" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_role" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "access_role_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "access_role" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "accounting_assetbase" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "asset_number" varchar(30) NULL UNIQUE, "serial_number" varchar(30) NULL UNIQUE, "asset_type" varchar(30) NOT NULL, "created" datetime NOT NULL, "modified" datetime NOT NULL, "organization_id" integer NOT NULL REFERENCES "access_tenant" ("id") DEFERRABLE INITIALLY DEFERRED, "model_notes" text NULL);
|
||||
CREATE TABLE IF NOT EXISTS "accounting_assetbase_audithistory" ("centurionaudit_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_audithistory" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "accounting_assetbase_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "django_admin_log" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "object_id" text NULL, "object_repr" varchar(200) NOT NULL, "action_flag" smallint unsigned NOT NULL CHECK ("action_flag" >= 0), "change_message" text NOT NULL, "content_type_id" integer NULL REFERENCES "django_content_type" ("id") DEFERRABLE INITIALLY DEFERRED, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "action_time" datetime NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "api_authtoken" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "note" varchar(50) NULL, "token" varchar(64) NOT NULL UNIQUE, "expires" datetime NOT NULL, "created" datetime NOT NULL, "modified" datetime NOT NULL, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "itam_itamassetbase" ("assetbase_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "accounting_assetbase" ("id") DEFERRABLE INITIALLY DEFERRED, "itam_type" varchar(30) NOT NULL);
|
||||
@ -197,6 +197,8 @@ CREATE TABLE IF NOT EXISTS "itam_softwarecategory_centurionmodelnote" ("centurio
|
||||
CREATE TABLE IF NOT EXISTS "itam_softwareversion" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "created" datetime NOT NULL, "modified" datetime NOT NULL, "name" varchar(50) NOT NULL, "organization_id" integer NOT NULL REFERENCES "access_tenant" ("id") DEFERRABLE INITIALLY DEFERRED, "software_id" integer NOT NULL REFERENCES "itam_software" ("id") DEFERRABLE INITIALLY DEFERRED, "model_notes" text NULL);
|
||||
CREATE TABLE IF NOT EXISTS "itam_softwareversion_audithistory" ("centurionaudit_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_audithistory" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "itam_softwareversion" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "itam_softwareversion_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "itam_softwareversion" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "itam_itamassetbase_audithistory" ("centurionaudit_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_audithistory" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "itam_itamassetbase" ("assetbase_ptr_id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "itam_itamassetbase_centurionmodelnote" ("centurionmodelnote_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "core_centurionmodelnote" ("id") DEFERRABLE INITIALLY DEFERRED, "model_id" integer NOT NULL REFERENCES "itam_itamassetbase" ("assetbase_ptr_id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "itim_cluster_devices" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "cluster_id" integer NOT NULL REFERENCES "itim_cluster" ("id") DEFERRABLE INITIALLY DEFERRED, "device_id" integer NOT NULL REFERENCES "itam_device" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "itim_cluster_nodes" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "cluster_id" integer NOT NULL REFERENCES "itim_cluster" ("id") DEFERRABLE INITIALLY DEFERRED, "device_id" integer NOT NULL REFERENCES "itam_device" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
CREATE TABLE IF NOT EXISTS "itim_cluster" ("model_notes" text NULL, "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(50) NOT NULL, "config" text NULL CHECK ((JSON_VALID("config") OR "config" IS NULL)), "created" datetime NOT NULL, "modified" datetime NOT NULL, "organization_id" integer NOT NULL REFERENCES "access_tenant" ("id") DEFERRABLE INITIALLY DEFERRED, "cluster_type_id" integer NULL REFERENCES "itim_clustertype" ("id") DEFERRABLE INITIALLY DEFERRED, "parent_cluster_id" integer NULL REFERENCES "itim_cluster" ("id") DEFERRABLE INITIALLY DEFERRED);
|
||||
@ -237,9 +239,9 @@ CREATE TABLE IF NOT EXISTS "social_auth_nonce" ("id" integer NOT NULL PRIMARY KE
|
||||
CREATE TABLE IF NOT EXISTS "social_auth_usersocialauth" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "provider" varchar(32) NOT NULL, "uid" varchar(255) NOT NULL, "user_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "created" datetime NOT NULL, "modified" datetime NOT NULL, "extra_data" text NOT NULL CHECK ((JSON_VALID("extra_data") OR "extra_data" IS NULL)));
|
||||
CREATE TABLE IF NOT EXISTS "social_auth_partial" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "token" varchar(32) NOT NULL, "next_step" smallint unsigned NOT NULL CHECK ("next_step" >= 0), "backend" varchar(32) NOT NULL, "timestamp" datetime NOT NULL, "data" text NOT NULL CHECK ((JSON_VALID("data") OR "data" IS NULL)));
|
||||
DELETE FROM sqlite_sequence;
|
||||
INSERT INTO sqlite_sequence VALUES('django_migrations',222);
|
||||
INSERT INTO sqlite_sequence VALUES('django_content_type',216);
|
||||
INSERT INTO sqlite_sequence VALUES('auth_permission',909);
|
||||
INSERT INTO sqlite_sequence VALUES('django_migrations',227);
|
||||
INSERT INTO sqlite_sequence VALUES('django_content_type',218);
|
||||
INSERT INTO sqlite_sequence VALUES('auth_permission',917);
|
||||
INSERT INTO sqlite_sequence VALUES('auth_group',0);
|
||||
INSERT INTO sqlite_sequence VALUES('auth_user',0);
|
||||
INSERT INTO sqlite_sequence VALUES('core_notes',0);
|
||||
@ -261,6 +263,8 @@ INSERT INTO sqlite_sequence VALUES('assistance_knowledgebasecategory',0);
|
||||
INSERT INTO sqlite_sequence VALUES('assistance_modelknowledgebasearticle',0);
|
||||
INSERT INTO sqlite_sequence VALUES('access_tenant',0);
|
||||
INSERT INTO sqlite_sequence VALUES('access_entity',0);
|
||||
INSERT INTO sqlite_sequence VALUES('access_role',0);
|
||||
INSERT INTO sqlite_sequence VALUES('accounting_assetbase',0);
|
||||
INSERT INTO sqlite_sequence VALUES('django_admin_log',0);
|
||||
INSERT INTO sqlite_sequence VALUES('itam_devicetype',0);
|
||||
INSERT INTO sqlite_sequence VALUES('config_management_configgrouphosts',0);
|
||||
|
@ -0,0 +1,81 @@
|
||||
# Generated by Django 5.1.10 on 2025-07-10 09:10
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
|
||||
("itam", "0024_alter_software_organization"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ITAMAssetBaseAuditHistory",
|
||||
fields=[
|
||||
(
|
||||
"centurionaudit_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="core.centurionaudit",
|
||||
),
|
||||
),
|
||||
(
|
||||
"model",
|
||||
models.ForeignKey(
|
||||
help_text="Model this history belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="itam.itamassetbase",
|
||||
verbose_name="Model",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "IT Asset History",
|
||||
"verbose_name_plural": "IT Asset Histories",
|
||||
"db_table": "itam_itamassetbase_audithistory",
|
||||
"managed": True,
|
||||
},
|
||||
bases=("core.centurionaudit",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ITAMAssetBaseCenturionModelNote",
|
||||
fields=[
|
||||
(
|
||||
"centurionmodelnote_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="core.centurionmodelnote",
|
||||
),
|
||||
),
|
||||
(
|
||||
"model",
|
||||
models.ForeignKey(
|
||||
help_text="Model this note belongs to",
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="itam.itamassetbase",
|
||||
verbose_name="Model",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "IT Asset Note",
|
||||
"verbose_name_plural": "IT Asset Notes",
|
||||
"db_table": "itam_itamassetbase_centurionmodelnote",
|
||||
"managed": True,
|
||||
},
|
||||
bases=("core.centurionmodelnote",),
|
||||
),
|
||||
]
|
@ -1,4 +1,5 @@
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
@ -18,9 +19,13 @@ class ITAMAssetBase(
|
||||
**Don't** use this model directly, it should be used via a sub-model.
|
||||
"""
|
||||
|
||||
_is_submodel = True
|
||||
|
||||
app_namespace = None
|
||||
|
||||
note_basename = 'accounting:_api_v2_asset_note'
|
||||
model_tag = 'it_asset'
|
||||
|
||||
url_model_name = 'itamassetbase'
|
||||
|
||||
|
||||
class Meta:
|
||||
@ -69,7 +74,7 @@ class ITAMAssetBase(
|
||||
|
||||
if(
|
||||
( isinstance(model, ITAMAssetBase) or issubclass(model, ITAMAssetBase) )
|
||||
and ITAMAssetBase._meta.itam_sub_model_type != 'itam_base'
|
||||
# and ITAMAssetBase._meta.itam_sub_model_type != 'itam_base'
|
||||
|
||||
):
|
||||
|
||||
@ -161,22 +166,7 @@ class ITAMAssetBase(
|
||||
|
||||
|
||||
|
||||
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):
|
||||
def clean_fields(self, exclude = None):
|
||||
|
||||
related_model = self.get_related_model()
|
||||
|
||||
@ -184,8 +174,56 @@ class ITAMAssetBase(
|
||||
|
||||
related_model = self
|
||||
|
||||
if self.itam_type != str(related_model._meta.itam_sub_model_type).lower().replace(' ', '_'):
|
||||
if(
|
||||
self.itam_type != str(related_model._meta.itam_sub_model_type).lower().replace(' ', '_')
|
||||
and str(related_model._meta.sub_model_type).lower().replace(' ', '_') != 'itam_base'
|
||||
):
|
||||
|
||||
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)
|
||||
|
||||
super().clean_fields(exclude = exclude)
|
||||
|
||||
|
||||
|
||||
def get_url(
|
||||
self, relative: bool = False, api_version: int = 2, many = False, request: any = None
|
||||
) -> str:
|
||||
|
||||
namespace = f'v{api_version}'
|
||||
|
||||
if self.get_app_namespace():
|
||||
namespace = namespace + ':' + self.get_app_namespace()
|
||||
|
||||
|
||||
url_basename = f'{namespace}:_api_{self._meta.model_name}'
|
||||
|
||||
if self.url_model_name:
|
||||
|
||||
url_basename = f'{namespace}:_api_{self.url_model_name}'
|
||||
|
||||
if (
|
||||
self._is_submodel
|
||||
and self._meta.sub_model_type != 'it_asset'
|
||||
):
|
||||
|
||||
url_basename += '_sub'
|
||||
|
||||
|
||||
if many:
|
||||
|
||||
url_basename += '-list'
|
||||
|
||||
else:
|
||||
|
||||
url_basename += '-detail'
|
||||
|
||||
|
||||
url = reverse( viewname = url_basename, kwargs = self.get_url_kwargs( many = many ) )
|
||||
|
||||
if not relative:
|
||||
|
||||
url = settings.SITE_URL + url
|
||||
|
||||
|
||||
return url
|
||||
|
@ -4,7 +4,7 @@ from drf_spectacular.utils import extend_schema_serializer
|
||||
|
||||
from access.serializers.organization import TenantBaseSerializer
|
||||
|
||||
from accounting.serializers.asset import (
|
||||
from accounting.serializers.assetbase import (
|
||||
BaseSerializer,
|
||||
ModelSerializer as AssetBaseModelSerializer,
|
||||
ViewSerializer as AssetBaseViewSerializer,
|
56
app/itam/serializers/centurionaudit_itamassetbase.py
Normal file
56
app/itam/serializers/centurionaudit_itamassetbase.py
Normal file
@ -0,0 +1,56 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from drf_spectacular.utils import extend_schema_serializer
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from centurion.models.meta import ITAMAssetBaseAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
|
||||
|
||||
from core.serializers.centurionaudit import (
|
||||
BaseSerializer,
|
||||
ViewSerializer as AuditHistoryViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'ITAMAssetBaseAuditHistoryModelSerializer')
|
||||
class ModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
BaseSerializer
|
||||
):
|
||||
"""Git Group Audit History Base Model"""
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = ITAMAssetBaseAuditHistory
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'display_name',
|
||||
'content_type',
|
||||
'model',
|
||||
'before',
|
||||
'after',
|
||||
'action',
|
||||
'user',
|
||||
'created',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'ITAMAssetBaseAuditHistoryViewSerializer')
|
||||
class ViewSerializer(
|
||||
ModelSerializer,
|
||||
AuditHistoryViewSerializer,
|
||||
):
|
||||
"""Git Group Audit History Base View Model"""
|
||||
pass
|
87
app/itam/serializers/centurionmodelnote_itamassetbase.py
Normal file
87
app/itam/serializers/centurionmodelnote_itamassetbase.py
Normal file
@ -0,0 +1,87 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from drf_spectacular.utils import extend_schema_serializer
|
||||
|
||||
from access.serializers.organization import (TenantBaseSerializer)
|
||||
|
||||
from centurion.models.meta import ITAMAssetBaseCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
|
||||
|
||||
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
|
||||
BaseSerializer,
|
||||
ModelSerializer as BaseModelModelSerializer,
|
||||
ViewSerializer as BaseModelViewSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'ITAMAssetBaseModelNoteModelSerializer')
|
||||
class ModelSerializer(
|
||||
BaseModelModelSerializer,
|
||||
):
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
return {
|
||||
'_self': item.get_url( request = self._context['view'].request ),
|
||||
}
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = ITAMAssetBaseCenturionModelNote
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'display_name',
|
||||
'body',
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'content_type',
|
||||
'model',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'organization',
|
||||
'created_by',
|
||||
'modified_by',
|
||||
'content_type',
|
||||
'model',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
|
||||
def validate(self, attrs):
|
||||
|
||||
is_valid = False
|
||||
|
||||
note_model = self.Meta.model.model.field.related_model
|
||||
|
||||
attrs['model'] = note_model.objects.get(
|
||||
id = int( self.context['view'].kwargs['model_id'] )
|
||||
)
|
||||
|
||||
|
||||
is_valid = super().validate(attrs)
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
@extend_schema_serializer(component_name = 'ITAMAssetBaseModelNoteViewSerializer')
|
||||
class ViewSerializer(
|
||||
ModelSerializer,
|
||||
BaseModelViewSerializer,
|
||||
):
|
||||
|
||||
organization = TenantBaseSerializer( many = False, read_only = True )
|
@ -18,7 +18,7 @@ def model(request):
|
||||
@pytest.fixture(scope='function')
|
||||
def create_serializer():
|
||||
|
||||
from itam.serializers.asset_it_asset import ModelSerializer
|
||||
from itam.serializers.assetbase_itamassetbase import ModelSerializer
|
||||
|
||||
|
||||
yield ModelSerializer
|
||||
|
@ -1,3 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from accounting.tests.functional.asset_base.test_functional_asset_base_metadata import AssetBaseMetadataInheritedCases
|
||||
@ -6,6 +8,7 @@ from itam.models.itam_asset_base import ITAMAssetBase
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_itamassetbase
|
||||
class MetadataTestCases(
|
||||
AssetBaseMetadataInheritedCases,
|
||||
):
|
||||
@ -22,7 +25,7 @@ class MetadataTestCases(
|
||||
|
||||
url_view_kwargs: dict = {}
|
||||
|
||||
url_name = '_api_v2_itam_asset'
|
||||
url_name = '_api_itamassetbase'
|
||||
|
||||
|
||||
|
||||
@ -38,6 +41,7 @@ class ITAMAssetBaseMetadataInheritedCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class ITAMAssetBaseMetadataTest(
|
||||
MetadataTestCases,
|
||||
TestCase,
|
||||
|
@ -1,7 +1,10 @@
|
||||
import pytest
|
||||
|
||||
from accounting.tests.functional.asset_base.test_functional_asset_base_permission import AssetBasePermissionsAPIInheritedCases
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_itamassetbase
|
||||
class PermissionsAPITestCases(
|
||||
AssetBasePermissionsAPIInheritedCases,
|
||||
):
|
||||
@ -17,13 +20,13 @@ class PermissionsAPITestCases(
|
||||
kwargs_create_item_diff_org: dict = {}
|
||||
|
||||
url_kwargs: dict = {
|
||||
'asset_model': 'it_asset',
|
||||
'model_name': 'itamassetbase',
|
||||
}
|
||||
|
||||
url_name = '_api_v2_itam_asset'
|
||||
url_name = '_api_itamassetbase'
|
||||
|
||||
url_view_kwargs: dict = {
|
||||
'asset_model': 'it_asset',
|
||||
'model_name': 'itamassetbase',
|
||||
}
|
||||
|
||||
|
||||
@ -40,6 +43,7 @@ class ITAMAssetBasePermissionsAPIInheritedCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class ITAMAssetBasePermissionsAPIPyTest(
|
||||
PermissionsAPITestCases,
|
||||
):
|
||||
|
@ -1,3 +1,4 @@
|
||||
import pytest
|
||||
|
||||
from accounting.tests.functional.asset_base.test_functional_asset_base_serializer import AssetBaseSerializerInheritedCases
|
||||
|
||||
@ -25,6 +26,7 @@ class MockView:
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_itamassetbase
|
||||
class ITAMAssetBaseSerializerTestCases(
|
||||
AssetBaseSerializerInheritedCases
|
||||
):
|
||||
@ -54,6 +56,7 @@ class ITAMAssetBaseSerializerInheritedCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class ITAMAssetBaseSerializerPyTest(
|
||||
ITAMAssetBaseSerializerTestCases,
|
||||
):
|
||||
|
@ -1,3 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from accounting.tests.functional.asset_base.test_functional_asset_base_viewset import AssetBaseViewSetInheritedCases
|
||||
@ -6,6 +8,7 @@ from itam.models.itam_asset_base import ITAMAssetBase
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_itamassetbase
|
||||
class ViewSetTestCases(
|
||||
AssetBaseViewSetInheritedCases
|
||||
):
|
||||
@ -21,14 +24,14 @@ class ViewSetTestCases(
|
||||
model = ITAMAssetBase
|
||||
|
||||
url_kwargs: dict = {
|
||||
'asset_model': 'it_asset',
|
||||
'model_name': 'itamassetbase',
|
||||
}
|
||||
|
||||
url_view_kwargs: dict = {
|
||||
'asset_model': 'it_asset',
|
||||
'model_name': 'itamassetbase',
|
||||
}
|
||||
|
||||
url_name = '_api_v2_itam_asset'
|
||||
url_name = '_api_itamassetbase'
|
||||
|
||||
|
||||
|
||||
@ -42,6 +45,7 @@ class ITAMAssetBaseViewSetInheritedCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class ITAMAssetBaseViewSetTest(
|
||||
ViewSetTestCases,
|
||||
TestCase,
|
||||
|
@ -1,14 +1,22 @@
|
||||
import pytest
|
||||
|
||||
from itam.models.itam_asset_base import ITAMAssetBase
|
||||
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def model(request):
|
||||
def model(model_itamassetbase):
|
||||
|
||||
request.cls.model = ITAMAssetBase
|
||||
yield model_itamassetbase
|
||||
|
||||
yield request.cls.model
|
||||
|
||||
del request.cls.model
|
||||
@pytest.fixture( scope = 'class', autouse = True)
|
||||
def model_kwargs(request, kwargs_itamassetbase):
|
||||
|
||||
request.cls.kwargs_create_item = kwargs_itamassetbase.copy()
|
||||
|
||||
yield kwargs_itamassetbase.copy()
|
||||
|
||||
if hasattr(request.cls, 'kwargs_create_item'):
|
||||
try:
|
||||
del request.cls.kwargs_create_item
|
||||
except:
|
||||
pass
|
||||
|
@ -1,9 +1,12 @@
|
||||
import pytest
|
||||
|
||||
from accounting.tests.unit.asset_base.test_unit_asset_base_api_fields import (
|
||||
AssetBaseAPIInheritedCases
|
||||
)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_itamassetbase
|
||||
class ITAMAssetBaseAPITestCases(
|
||||
AssetBaseAPIInheritedCases,
|
||||
):
|
||||
@ -29,6 +32,7 @@ class ITAMAssetBaseAPIInheritedCases(
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class ITAMAssetBaseAPIPyTest(
|
||||
ITAMAssetBaseAPITestCases,
|
||||
):
|
||||
|
@ -1,177 +1,77 @@
|
||||
import pytest
|
||||
|
||||
from django.db import models
|
||||
|
||||
from accounting.tests.unit.asset_base.test_unit_asset_base_model import AssetBaseModelInheritedCases
|
||||
from accounting.tests.unit.asset_base.test_unit_asset_base_model import (
|
||||
AssetBaseModelInheritedCases,
|
||||
)
|
||||
|
||||
from itam.models.itam_asset_base import ITAMAssetBase
|
||||
|
||||
|
||||
|
||||
class ITAMAssetBaseModelTestCases(
|
||||
AssetBaseModelInheritedCases,
|
||||
@pytest.mark.model_itamassetbase
|
||||
class ITAMAssetModelTestCases(
|
||||
AssetBaseModelInheritedCases
|
||||
):
|
||||
|
||||
kwargs_create_item: dict = {}
|
||||
|
||||
it_asset_base_model = ITAMAssetBase
|
||||
@property
|
||||
def parameterized_class_attributes(self):
|
||||
|
||||
sub_model_type = 'itam_base'
|
||||
"""Sub Model Type
|
||||
|
||||
sub-models must have this attribute defined in `ModelName.Meta.sub_model_type`
|
||||
"""
|
||||
return {
|
||||
'_is_submodel': {
|
||||
'value': True
|
||||
},
|
||||
'app_namespace': {
|
||||
'type': type(None),
|
||||
'value': None
|
||||
},
|
||||
'model_tag': {
|
||||
'type': str,
|
||||
'value': 'it_asset'
|
||||
},
|
||||
'url_model_name': {
|
||||
'type': str,
|
||||
'value': 'itamassetbase'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@property
|
||||
def parameterized_model_fields(self):
|
||||
|
||||
return {
|
||||
'itam_type': {
|
||||
'blank': True,
|
||||
'default': 'itam_base',
|
||||
'field_type': models.CharField,
|
||||
'max_length': 30,
|
||||
'null': False,
|
||||
'unique': False,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
def test_class_inherits_itam_assetbase(self):
|
||||
def test_class_inherits_itamassetbase(self, model):
|
||||
""" Class inheritence
|
||||
|
||||
TenancyObject must inherit SaveHistory
|
||||
"""
|
||||
|
||||
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
|
||||
assert issubclass(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
|
||||
|
||||
|
||||
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'
|
||||
|
||||
|
||||
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,
|
||||
class ITAMAssetModelInheritedCases(
|
||||
ITAMAssetModelTestCases,
|
||||
):
|
||||
"""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`
|
||||
"""
|
||||
|
||||
|
||||
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
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ITAMAssetBaseModelPyTest(
|
||||
ITAMAssetBaseModelTestCases,
|
||||
@pytest.mark.module_accounting
|
||||
class ITAMAssetModelPyTest(
|
||||
ITAMAssetModelTestCases,
|
||||
):
|
||||
|
||||
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
|
||||
pass
|
||||
|
@ -1,3 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
@ -22,6 +24,7 @@ from itam.models.itam_asset_base import ITAMAssetBase
|
||||
|
||||
|
||||
|
||||
@pytest.mark.model_itamassetbase
|
||||
class ITAMAssetBaseViewsetTestCases(
|
||||
AssetBaseViewsetInheritedCases,
|
||||
):
|
||||
@ -41,10 +44,11 @@ class ITAMAssetBaseViewsetInheritedCases(
|
||||
model: str = None
|
||||
"""name of the model to test"""
|
||||
|
||||
route_name = 'v2:accounting:_api_v2_asset_sub'
|
||||
route_name = 'v2:accounting:_api_asset_sub'
|
||||
|
||||
|
||||
|
||||
@pytest.mark.module_accounting
|
||||
class ITAMAssetBaseViewsetTest(
|
||||
ITAMAssetBaseViewsetTestCases,
|
||||
TestCase,
|
||||
|
@ -34,8 +34,8 @@ router.register(
|
||||
basename = '_api_v2_itam_home'
|
||||
)
|
||||
router.register(
|
||||
prefix = '(?P<asset_model>[it_asset]+)', viewset = asset.ViewSet,
|
||||
feature_flag = '2025-00007', basename = '_api_v2_itam_asset'
|
||||
prefix = '(?P<model_name>[itamassetbase]+)', viewset = asset.ViewSet,
|
||||
feature_flag = '2025-00007', basename = '_api_itamassetbase'
|
||||
)
|
||||
router.register(
|
||||
prefix = 'device', viewset = device.ViewSet,
|
||||
|
15
app/tests/fixtures/__init__.py
vendored
15
app/tests/fixtures/__init__.py
vendored
@ -18,6 +18,11 @@ from .model_appsettings import (
|
||||
model_appsettings,
|
||||
)
|
||||
|
||||
from .model_assetbase import (
|
||||
kwargs_assetbase,
|
||||
model_assetbase,
|
||||
)
|
||||
|
||||
from .model_centurionaudit import (
|
||||
kwargs_centurionaudit,
|
||||
model_centurionaudit,
|
||||
@ -160,6 +165,11 @@ from .model_instance import (
|
||||
model_instance
|
||||
)
|
||||
|
||||
from .model_itamassetbase import (
|
||||
kwargs_itamassetbase,
|
||||
model_itamassetbase,
|
||||
)
|
||||
|
||||
from .model_knowledgebase import (
|
||||
kwargs_knowledgebase,
|
||||
model_knowledgebase,
|
||||
@ -223,6 +233,11 @@ from .model_projecttype import (
|
||||
model_projecttype,
|
||||
)
|
||||
|
||||
from .model_role import (
|
||||
kwargs_role,
|
||||
model_role,
|
||||
)
|
||||
|
||||
from .model_service import (
|
||||
kwargs_service,
|
||||
model_service,
|
||||
|
26
app/tests/fixtures/model_assetbase.py
vendored
Normal file
26
app/tests/fixtures/model_assetbase.py
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
import datetime
|
||||
import pytest
|
||||
|
||||
from accounting.models.asset_base import AssetBase
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def model_assetbase():
|
||||
|
||||
yield AssetBase
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def kwargs_assetbase( kwargs_centurionmodel, model_assetbase ):
|
||||
|
||||
random_str = str(datetime.datetime.now(tz=datetime.timezone.utc))
|
||||
random_str = str(random_str).replace(
|
||||
' ', '').replace(':', '').replace('+', '').replace('.', '')
|
||||
|
||||
kwargs = {
|
||||
**kwargs_centurionmodel.copy(),
|
||||
'asset_number': 'ab_' + random_str,
|
||||
'serial_number': 'ab_' + random_str,
|
||||
# 'asset_type': (model_assetbase._meta.sub_model_type, model_assetbase._meta.verbose_name),
|
||||
}
|
||||
|
||||
yield kwargs.copy()
|
25
app/tests/fixtures/model_itamassetbase.py
vendored
Normal file
25
app/tests/fixtures/model_itamassetbase.py
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
import datetime
|
||||
import pytest
|
||||
|
||||
from itam.models.itam_asset_base import ITAMAssetBase
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def model_itamassetbase():
|
||||
|
||||
yield ITAMAssetBase
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def kwargs_itamassetbase( kwargs_assetbase, model_itamassetbase ):
|
||||
|
||||
random_str = str(datetime.datetime.now(tz=datetime.timezone.utc))
|
||||
random_str = str(random_str).replace(
|
||||
' ', '').replace(':', '').replace('+', '').replace('.', '')
|
||||
|
||||
kwargs = {
|
||||
**kwargs_assetbase.copy(),
|
||||
# 'asset_type': (model_itamassetbase._meta.sub_model_type, model_itamassetbase._meta.verbose_name),
|
||||
'itam_type': "it_asset"
|
||||
}
|
||||
|
||||
yield kwargs.copy()
|
21
app/tests/fixtures/model_kwarg_data.py
vendored
21
app/tests/fixtures/model_kwarg_data.py
vendored
@ -1,5 +1,5 @@
|
||||
import datetime
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
||||
import pytest
|
||||
|
||||
from django.db import models
|
||||
@ -97,9 +97,22 @@ def model_kwarg_data():
|
||||
|
||||
if 'unique' in e.error_dict['__all__'][0].code:
|
||||
|
||||
instance = model.objects.get(
|
||||
**kwargs
|
||||
)
|
||||
try:
|
||||
|
||||
instance = model.objects.get(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
except ObjectDoesNotExist as e:
|
||||
|
||||
if 'modified' in kwargs:
|
||||
|
||||
no_modified_in_kwargs = kwargs.copy()
|
||||
del no_modified_in_kwargs['modified']
|
||||
|
||||
instance = model.objects.get(
|
||||
**no_modified_in_kwargs
|
||||
)
|
||||
|
||||
|
||||
for field, values in many_field.items():
|
||||
|
29
app/tests/fixtures/model_role.py
vendored
Normal file
29
app/tests/fixtures/model_role.py
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
import datetime
|
||||
import pytest
|
||||
|
||||
from access.models.role import Role
|
||||
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def model_role():
|
||||
|
||||
yield Role
|
||||
|
||||
|
||||
@pytest.fixture( scope = 'class')
|
||||
def kwargs_role(
|
||||
kwargs_centurionmodel
|
||||
):
|
||||
|
||||
random_str = str(datetime.datetime.now(tz=datetime.timezone.utc))
|
||||
random_str = str(random_str).replace(
|
||||
' ', '').replace(':', '').replace('-', '').replace('+', '').replace('.', '')
|
||||
|
||||
kwargs = {
|
||||
**kwargs_centurionmodel.copy(),
|
||||
'name': 'r_' + random_str,
|
||||
'modified': '2024-06-03T23:00:00Z',
|
||||
}
|
||||
|
||||
yield kwargs.copy()
|
@ -87,7 +87,7 @@ A Model link is a reference to an item within the database. Supported model link
|
||||
| gitrepository| `$git_repository-<id>` |
|
||||
| gitgroup| `$git_group-<id>` |
|
||||
| group| `$-<id>` |
|
||||
| it_asset | `$it_asset-<id>` |
|
||||
| it_asset | `$asset-<id>` or `$it_asset-<id>` |
|
||||
| knowledgebase| `$kb-<id>` |
|
||||
| knowledgebasecategory| `$kb_category-<id>` |
|
||||
| manufacturer| `$manufacturer-<id>` |
|
||||
|
@ -1103,6 +1103,7 @@ markers = [
|
||||
"meta_models: Selects Meta models",
|
||||
"mixin: Selects all mixin test cases.",
|
||||
"mixin_centurion: Selects all centurion mixin test cases.",
|
||||
"model_assetbase: Selects tests for model Asset Base.",
|
||||
"model_appsettings: Selects tests for model app settings.",
|
||||
"model_company: Selects test for model Company.",
|
||||
"model_configgroups: Selects Config Group tests.",
|
||||
@ -1124,6 +1125,7 @@ markers = [
|
||||
"model_githubrepository: Selects tests for model `github repository`",
|
||||
"model_gitlabrepository: Selects tests for model `gitlab repository`",
|
||||
"model_gitrepository: Selects tests for model `git repository`",
|
||||
"model_itamassetbase: Selects tests for model ITAM Asset Base.",
|
||||
"model_knowledgebase: Selects Knowledge base tests.",
|
||||
"model_knowledgebasecategory: Selects Knowledge base category tests.",
|
||||
"model_manufacturer: Select all manufacturer tests.",
|
||||
@ -1135,6 +1137,7 @@ markers = [
|
||||
"model_projectmilestone: Selects tests for model Project Milestone.",
|
||||
"model_projectstate: Selects tests for model Project State.",
|
||||
"model_projecttype: Selects tests for model Project Type.",
|
||||
"model_role: Selects tests for model Role.",
|
||||
"model_service: Selects tests for model Service.",
|
||||
"model_software: Selects tests for model Software.",
|
||||
"model_softwarecategory: Selects tests for model Software Category.",
|
||||
|
Reference in New Issue
Block a user