Compare commits

...

793 Commits

Author SHA1 Message Date
Jon
6a3df990c4 Merge pull request #848 from nofusscomputing/devops-tests 2025-06-22 17:03:33 +09:30
Jon
893b368776 fix(devops): git repository is sub-model ViewSet must inherit from SubModel
ref: #848 #809 #810 #811
2025-06-22 16:26:58 +09:30
Jon
dea1daed98 Merge pull request #847 from nofusscomputing/model-inheritance-tests 2025-06-21 16:14:23 +09:30
Jon
92760a6a0c chore(access): Update common viewset test suite mock request
ref: #847 #840 #841 #842 #843 #845
2025-06-21 15:33:02 +09:30
Jon
c743ddf127 refactor(access): Update Entity model ViewSet attribute model_kwarg to model_name
ref: #847 #840 #841 #842 #843 #845
2025-06-21 14:26:59 +09:30
Jon
29959759da refactor(access): Update Entity model ViewSet to inherit from submodel-rewrite
ref: #847 #840 #841 #842 #843 #845
2025-06-21 14:22:21 +09:30
Jon
52ab68c4fb chore(access): remove get_url_kwargs function as it's not required
ref: #847 #840 #841 #842 #843 #845
2025-06-21 14:21:16 +09:30
Jon
1639d06ff4 fix(access): entity field entity_type is an auto field
ref: #847
2025-06-21 13:28:36 +09:30
Jon
b8a1e73400 chore: Ensure employee fixture creates integer within max of field
ref: #847
2025-06-21 13:05:54 +09:30
Jon
3b738b7897 chore: update apifield test suit to set model class var and for the removal of fields property from models
ref: #847
2025-06-21 12:27:01 +09:30
Jon
5c6ba7412f Merge pull request #839 from nofusscomputing/model-inheritance 2025-06-20 06:37:08 +09:30
Jon
bc19a9b6f9 chore(test): update db fixture
ref: #839
2025-06-20 00:28:06 +09:30
Jon
3f7838ac26 docs(issue_template): remove notes and history requirements from new model issue template
ref: #839
2025-06-20 00:22:47 +09:30
Jon
e2cbf7eadd chore(access): Add pytest mark for Model Entity Tests
ref: #839 closes #840
2025-06-20 00:00:20 +09:30
Jon
580ea43cad chore(access): Add pytest mark for Model Company Tests
ref: #839 closes #845
2025-06-20 00:00:20 +09:30
Jon
86d0b47a7b feat(human_resources): Add model tag for Employee model
ref: #839 closes #843
2025-06-20 00:00:20 +09:30
Jon
52a7475c95 refactor(access): Update Test Suite for Employee model
ref: #839 #843
2025-06-20 00:00:20 +09:30
Jon
423cd7d52b feat(human_resources): Add AuditHistory Serializer for Employee model
ref: #839 #843
2025-06-20 00:00:20 +09:30
Jon
a0107899b9 feat(human_resources): Add Notes Serializer for Employee model
ref: #839 #843
2025-06-20 00:00:20 +09:30
Jon
cc19d56549 feat(human_resources): Change model to inherit from CenturionModel for Employee model
ref: #839 #843
2025-06-20 00:00:20 +09:30
Jon
9e44405248 feat(access): Add model tag for Person model
ref: #839 closes #841
2025-06-20 00:00:20 +09:30
Jon
5b0cd33dfc refactor(access): Update Test Suite for Person model
ref: #839 #841
2025-06-20 00:00:20 +09:30
Jon
c26355f7f4 feat(access): Add AuditHistory Serializer for Person model
ref: #839 #841
2025-06-20 00:00:20 +09:30
Jon
46af371540 feat(access): Add Notes Serializer for Person model
ref: #839 #841
2025-06-20 00:00:20 +09:30
Jon
dc146bbf9f feat(access): Change model to inherit from CenturionModel for Person model
ref: #839 #841
2025-06-20 00:00:20 +09:30
Jon
bd48a96f35 feat(access): Add model tag for Contact model
ref: #839 closes #842
2025-06-20 00:00:20 +09:30
Jon
c2cb179e50 refactor(access): Update Test Suite for Contact model
ref: #839 #842
2025-06-20 00:00:20 +09:30
Jon
b55c41be7f feat(access): Add AuditHistory Serializer for Contact model
ref: #839 #842
2025-06-20 00:00:20 +09:30
Jon
8d4692efd7 feat(access): Add Notes Serializer for Contact model
ref: #839 #842
2025-06-20 00:00:19 +09:30
Jon
48068632f5 feat(access): Change model to inherit from CenturionModel for Contact model
ref: #839 #842
2025-06-20 00:00:19 +09:30
Jon
ecdb6fd18d refactor(access): Update Test Suite for Company model
ref: #839 #845 #760
2025-06-20 00:00:19 +09:30
Jon
5522e7f6ed feat(access): Add AuditHistory Serializer for Company model
ref: #839 #845 #760
2025-06-20 00:00:19 +09:30
Jon
dca87f51e0 feat(access): Add Notes Serializer for Entity model
ref: #839 #845 #760
2025-06-20 00:00:19 +09:30
Jon
43f53a5ad8 feat(access): Change model to inherit from CenturionModel for Company model
ref: #839 #845 #760
2025-06-20 00:00:19 +09:30
Jon
7a69076e5d feat(access): Change model to inherit from CenturionModel for Entity model
ref: #839 closes #840
2025-06-20 00:00:19 +09:30
Jon
e6d3f8d476 refactor(access): Update URL route name for Entity model
ref: #839 #840 #841 #842 #843 #844 #845
2025-06-20 00:00:19 +09:30
Jon
3307febf14 refactor(access): Update Test Suite for Entity model
ref: #839 #840
2025-06-20 00:00:19 +09:30
Jon
0b186616ae feat(access): Add AuditHistory Serializer for Entity model
ref: #839 #840
2025-06-20 00:00:19 +09:30
Jon
d94e4c0f77 feat(access): Add Notes Serializer for Entity model
ref: #839 #840
2025-06-20 00:00:19 +09:30
Jon
2b10183f5c feat(access): Change model to inherit from CenturionModel for Entity model
ref: #839 #840
2025-06-20 00:00:19 +09:30
Jon
14d5198058 chore: enable creating reandom email field in model_kwarg_data fixture
ref: #839
2025-06-19 21:22:55 +09:30
Jon
69e75043a6 chore: make model_instance a function fixture
ref: #839
2025-06-17 16:17:38 +09:30
Jon
8aacbe158b test(settings): for api checks for model AppSettings, make user super_user
required so that tests can run

ref: #839
2025-06-17 15:35:29 +09:30
Jon
019d6240a9 test(settings): Exclude inter-org tests for model AppSettings
perms for appsettings are global

ref: #839 #834
2025-06-17 15:15:13 +09:30
Jon
a36329201d refactor(access): Update is_tenancy_object to check for CenturionModel
ref: #839 #767
2025-06-17 15:13:46 +09:30
Jon
9cf8d32f43 Merge pull request #833 from nofusscomputing/model-inheritance 2025-06-16 19:16:16 +09:30
Jon
ef5f913830 test(settings): Remove old API Permission tests no longer required
ref: #833 #836
2025-06-16 18:07:47 +09:30
Jon
43e89aa501 test(settings): Ensure ExternalLink model hasrequired field template added
ref: #833 #835
2025-06-16 18:07:01 +09:30
Jon
4c837be6a6 chore(api): Add support to API Permission to include additional tests
ref: #833
2025-06-16 18:04:49 +09:30
Jon
7637c8c583 refactor(access): For request middleware, use filter and first object so that testing can occur when mre than one exists
ref: #833
2025-06-16 17:09:53 +09:30
Jon
df5d23c79b test(api): if model lacks list endpoint, check if method alllowed for test cases for Functional API perms test suite
ref: #833
2025-06-16 17:09:06 +09:30
Jon
9a65e67f33 test(api): if model lacks organization field, xfail returned orgs test cases for Functional API perms test suite
ref: #833
2025-06-16 17:08:13 +09:30
Jon
ef24e41b44 fix(access): Ensure that if method not allowed, exception is thrown first before perms check
ref: #833
2025-06-16 16:22:47 +09:30
Jon
f71fcd0381 test: Ensure service fixture assosiates with device
ref: #833 #828
2025-06-16 16:22:07 +09:30
Jon
e778d5b2a0 test(api): if model lacks organization field, xfail returned orgs test cases for Functional API perms test suite
ref: #833 #820
2025-06-16 15:49:27 +09:30
Jon
96a9a16326 fix(itam): Model software must be related linked to organization model
ref: #833 #820
2025-06-16 15:48:32 +09:30
Jon
838553577e test: Add depreciated models to be excluded from coverage
ref: #833
2025-06-16 14:59:18 +09:30
Jon
f8a00a3d5c chore: correct docs linting error
ref: #833
2025-06-16 14:24:57 +09:30
Jon
177f4863d3 feat(settings): Add model tag for ExtrnalLink model
ref: #833 #835 closes #584
2025-06-16 14:19:01 +09:30
Jon
c1707e84f7 refactor(settings): Update URL route name for UserSettings model
ref: #833 closes #836
2025-06-16 14:12:36 +09:30
Jon
092bf8f621 refactor(settings): Update Test Suite for ExternalLink model
ref: #833 #836
2025-06-16 14:11:32 +09:30
Jon
d71e9a692c feat(settings): Add AuditHistory Serializer for UserSettings model
ref: #833 #836
2025-06-16 14:07:02 +09:30
Jon
eb036e77b8 feat(settings): Add Notes Serializer for UserSettings model
ref: #833 #836
2025-06-16 14:06:43 +09:30
Jon
0ab6cda35f feat(settings): Change model to inherit from CenturionModel for UserSettings model
ref: #833 #836
2025-06-16 14:06:23 +09:30
Jon
368ef0dc4e feat(settings): Add model ExternalLink to migrate for history and notes
ref: #833 closes #835
2025-06-16 13:41:02 +09:30
Jon
a57512f2d0 refactor(settings): Update URL route name for ExternalLink model
ref: #833 #835
2025-06-16 13:40:47 +09:30
Jon
8f6ee7732a refactor(settings): Update Test Suite for ExternalLink model
ref: #833 #835
2025-06-16 13:39:47 +09:30
Jon
c5af5a46c9 feat(settings): Add AuditHistory Serializer for ExternalLink model
ref: #833 #835
2025-06-16 13:38:59 +09:30
Jon
016f3d70d4 feat(settings): Add Notes Serializer for ExternalLink model
ref: #833 #835
2025-06-16 13:38:22 +09:30
Jon
ce73f9a87a feat(settings): Change model to inherit from CenturionModel for ExternalSettings model
ref: #833 #835
2025-06-16 13:37:59 +09:30
Jon
a921d110da feat(settings): Add model AppSettings to migrate for history and notes
ref: #833 closes #834
2025-06-16 12:55:47 +09:30
Jon
0aa1e49505 refactor(settings): Update URL route name for AppSettings model
ref: #833 #834
2025-06-16 12:55:19 +09:30
Jon
67cba6f65f fix(access): if user has no orgs, dont filter by for query
ref: #833
2025-06-16 12:53:48 +09:30
Jon
f8c18e24a7 refactor(settings): Update Test Suite for AppSettings model
ref: #833 #834
2025-06-15 14:24:48 +09:30
Jon
581f49206e feat(settings): Add AuditHistory Serializer for AppSettings model
ref: #833 #834
2025-06-15 14:10:57 +09:30
Jon
484bbafe99 feat(settings): Add Notes Serializer for AppSettings model
ref: #833 #834
2025-06-15 14:10:25 +09:30
Jon
cc65b85145 feat(settings): Change model to inherit from CenturionModel for AppSettings model
ref: #833 #834
2025-06-15 14:10:02 +09:30
Jon
9f61264c5d chore: Add support for testing tenant model to fixture model_instance
ref: #833
2025-06-15 13:27:01 +09:30
Jon
4ad40f8b57 chore: skip depreciated linked ticket tests
ref: #833
2025-06-14 14:51:02 +09:30
Jon
7cdcdb7d58 feat(project_management): Add model ProjectType to migrate for history and notes
ref: #833 closes #832
2025-06-14 14:31:14 +09:30
Jon
c062fe7c5f refactor(project_management): Update URL route name for ProjectType model
ref: #833 #832
2025-06-14 14:31:00 +09:30
Jon
12f2dae154 refactor(project_management): Update Test Suite for ProjectType model
ref: #833 #832
2025-06-14 14:29:05 +09:30
Jon
3b7fda19ca feat(project_management): Add AuditHistory Serializer for ProjectTYpe model
ref: #833 #832
2025-06-14 14:00:21 +09:30
Jon
908dc8fed9 feat(project_management): Add Notes Serializer for ProjectType model
ref: #833 #832
2025-06-14 14:00:08 +09:30
Jon
9b02834662 feat(project_management): Change model to inherit from CenturionModel for ProjectType model
ref: #830 #832 closes #594
2025-06-14 13:59:35 +09:30
Jon
cdf1c0f12f feat(project_management): Add model ProjectState to migrate for history and notes
ref: #833 closes #831
2025-06-14 13:42:57 +09:30
Jon
6319d35948 refactor(project_management): Update URL route name for ProjectState model
ref: #833 #831
2025-06-14 13:42:41 +09:30
Jon
5a2dd9c4f6 refactor(project_management): Update Test Suite for ProjectState model
ref: #833 #831
2025-06-14 13:42:14 +09:30
Jon
67fa2a9ee7 feat(project_management): Add AuditHistory Serializer for ProjectState model
ref: #833 #831
2025-06-14 13:29:50 +09:30
Jon
42579611fc feat(project_management): Add Notes Serializer for ProjectState model
ref: #833 #831
2025-06-14 13:29:34 +09:30
Jon
174a457923 feat(project_management): Change model to inherit from CenturionModel for ProjectState model
ref: #830 #831
2025-06-14 13:29:11 +09:30
Jon
72f3aa53a2 feat(project_management): Add model ProjectMilestone to migrate for history and notes
ref: #833 closes #830
2025-06-14 13:03:00 +09:30
Jon
f959190a9a refactor(project_management): Update URL route name for ProjectMilestone model
ref: #833 #830
2025-06-14 13:02:26 +09:30
Jon
8fa5f7d5c0 refactor(project_management): Update Test Suite for ProjectMilestone model
ref: #833 #830
2025-06-14 13:01:41 +09:30
Jon
61b17811c4 feat(project_management): Add AuditHistory Serializer for ProjectMilestone model
ref: #833 #830
2025-06-14 12:59:37 +09:30
Jon
a1a758b785 feat(project_management): Add Notes Serializer for ProjectMilestone model
ref: #833 #830
2025-06-14 12:59:07 +09:30
Jon
2a8108dbc8 feat(project_management): Change model to inherit from CenturionModel for ProjectManagement model
ref: #833 #830 closes #591
2025-06-14 12:57:10 +09:30
Jon
9445159106 Merge pull request #824 from nofusscomputing/model-inheritance 2025-06-13 17:51:18 +09:30
Jon
3c13232b63 chore: correct tests
ref: #824
2025-06-13 17:35:58 +09:30
Jon
88ddc95725 chore: fix accessing var that dont exist
ref: #824
2025-06-13 17:16:47 +09:30
Jon
e67b154cae chore: remove depreciated docs
ref: #824 #343 #757
2025-06-13 17:09:55 +09:30
Jon
741101fe1c chore: fix accessing var that dont exist
ref: #824
2025-06-13 16:52:14 +09:30
Jon
d220b25eb5 feat(project_management): Add model Project to migrate for history and notes
ref: #824 closes #829
2025-06-13 16:22:19 +09:30
Jon
8aee5c59cc refactor(project_management): Update URL route name for Project model
ref: #824 #829
2025-06-13 16:21:24 +09:30
Jon
b1a0d1178a refactor(project_management): Update Test Suite for Project model
ref: #824 #829
2025-06-13 16:20:30 +09:30
Jon
75f97c3ef4 feat(project_management): Add AuditHistory Serializer for Project model
ref: #824 #829
2025-06-13 16:05:53 +09:30
Jon
48eb0a8206 feat(project_management): Add Notes Serializer for Project model
ref: #824 #829
2025-06-13 16:02:30 +09:30
Jon
77b1cc8a1e feat(project_management): Change model to inherit from CenturionModel for Project model
ref: #824 #829 closes #590
2025-06-13 16:01:59 +09:30
Jon
e91b6aeceb feat(itim): Add model Service to migrate for history and notes
ref: #824 closes #828
2025-06-13 15:12:37 +09:30
Jon
25c69540ec refactor(itim): Update URL route name for Service model
ref: #824 #828
2025-06-13 15:12:21 +09:30
Jon
df3d18281d refactor(itim): Update Test Suite for Service model
ref: #824 #828
2025-06-13 15:11:42 +09:30
Jon
c5ba8603b6 feat(itim): Add AuditHistory Serializer for Service model
ref: #824 #828
2025-06-13 13:55:16 +09:30
Jon
288ae09c1b feat(itim): Add Notes Serializer for Service model
ref: #824 #828
2025-06-13 13:55:01 +09:30
Jon
cbf935d06c feat(itim): Change model to inherit from CenturionModel for Service model
ref: #824 #828
2025-06-13 13:54:41 +09:30
Jon
c2934dae8b feat(itim): Add model Port to migrate for history and notes
ref: #824 closes #827
2025-06-13 13:25:23 +09:30
Jon
fcf6c4db03 refactor(itim): Update URL route name for Port model
ref: #824 #827
2025-06-13 13:24:46 +09:30
Jon
0a20adcdc8 refactor(itim): Update Test Suite for Port model
ref: #824 #827
2025-06-13 13:23:41 +09:30
Jon
7c871737cd feat(itim): Add AuditHistory Serializer for Port model
ref: #824 #827
2025-06-13 13:21:18 +09:30
Jon
8578d80fbd feat(itim): Add Notes Serializer for Port model
ref: #824 #827
2025-06-13 13:21:07 +09:30
Jon
e3e639682b feat(itim): Change model to inherit from CenturionModel for Port model
ref: #824 #827 closes #589
2025-06-13 13:20:32 +09:30
Jon
5714862c7a refactor(itim): Update URL route name for ClusterType model
ref: #824 closes #826 #580
2025-06-13 12:58:12 +09:30
Jon
7890af7984 refactor(itim): Update Test Suite for ClusterType model
ref: #824 #826
2025-06-13 12:52:38 +09:30
Jon
bf0f4c4d15 feat(itim): Add AuditHistory Serializer for ClusterType model
ref: #824 #826
2025-06-13 12:51:42 +09:30
Jon
efd60f994d feat(itim): Add Notes Serializer for ClusterType model
ref: #824 #826
2025-06-13 12:51:32 +09:30
Jon
2020552814 feat(itim): Change model to inherit from CenturionModel for ClusterType model
ref: #824 #826
2025-06-13 12:46:11 +09:30
Jon
24e17948bd feat(itim): Add model Cluster to migrate for history and notes
ref: #824 closes #825
2025-06-13 12:20:41 +09:30
Jon
4e728df6c2 refactor(itim): Update URL route name for Cluster model
ref: #824 #825
2025-06-13 12:20:23 +09:30
Jon
988b990e23 refactor(itim): Update Test Suite for Cluster model
ref: #824 #825
2025-06-13 12:18:14 +09:30
Jon
141923460a feat(itim): Add Notes Serializer for Cluster model
ref: #824 #825
2025-06-13 12:01:05 +09:30
Jon
550d4d9a43 feat(itim): Add AuditHistory Serializer for Cluster model
ref: #824 #825
2025-06-13 12:00:52 +09:30
Jon
f31598b33f feat(itim): Change model to inherit from CenturionModel for Cluster model
ref: #824 #825
2025-06-13 11:58:41 +09:30
Jon
506ce7ecd5 revert(core): return settings_value template tag
ref: #824
2025-06-13 10:55:02 +09:30
Jon
0aad84f296 Merge pull request #823 from nofusscomputing/model-inheritance 2025-06-12 13:06:07 +09:30
Jon
a9209ab5d2 feat(itam): Add model SoftwareVersion to migrate for history and notes
ref: #823 closes #822
2025-06-12 12:50:43 +09:30
Jon
cf83ec0886 refactor(itam): Update Test Suite for SoftwareVersion model
ref: #823 #822
2025-06-12 12:50:18 +09:30
Jon
1f578a0a6b refactor(itam): Update URL route name for SoftwareVersion model
ref: #823 #822
2025-06-12 12:49:46 +09:30
Jon
f0049d1639 feat(itam): Add Notes Serializer for SoftwareVersiony model
ref: #823 #822
2025-06-12 12:48:41 +09:30
Jon
0e2f1309ff feat(itam): Add AuditHistory Serializer for SoftwareVersion model
ref: #823 #822
2025-06-12 12:48:23 +09:30
Jon
511a9b6cb0 feat(itam): Change model to inherit from CenturionModel for SoftwareVersion model
ref: #823 #822
2025-06-12 12:48:08 +09:30
Jon
63d257dcf8 feat(itam): Add model SoftwareCategory to migrate for history and notes
ref: #823 closes #821
2025-06-12 12:26:45 +09:30
Jon
c7bd251bc0 refactor(itam): Update Test Suite for SoftwareCategory model
ref: #823 #821
2025-06-12 12:26:23 +09:30
Jon
3b56f4cee2 refactor(itam): Update URL route name for SoftwareCategory model
ref: #823 #820
2025-06-12 12:22:17 +09:30
Jon
919bc274c8 feat(itam): Add Notes Serializer for SoftwareCategory model
ref: #823 #821
2025-06-12 12:20:35 +09:30
Jon
19e864b8e2 feat(itam): Add AuditHistory Serializer for SoftwareCategory model
ref: #823 #821
2025-06-12 12:20:19 +09:30
Jon
fb8427c253 feat(itam): Change model to inherit from CenturionModel for SoftwareCategory model
ref: #823 #821 closes #595
2025-06-12 12:19:19 +09:30
Jon
2335493709 feat(itam): Add model Software to migrate for history and notes
ref: #823 #820
2025-06-12 12:05:38 +09:30
Jon
b68b49080c refactor: cater for dev that does not exist in test cleanup
ref: #823
2025-06-12 11:55:13 +09:30
Jon
6b80e86520 refactor(itam): Update Test Suite for Software model
ref: #823 closes #820
2025-06-12 11:46:50 +09:30
Jon
12f7e0575a refactor(itam): Update URL route name for Software model
ref: #823 #820
2025-06-12 11:44:39 +09:30
Jon
c1ce204e12 feat(itam): Add Notes Serializer for Software model
ref: #823 #820
2025-06-12 11:43:27 +09:30
Jon
e67aedc06c feat(itam): Add AuditHistory Serializer for Software model
ref: #823 #820
2025-06-12 11:43:12 +09:30
Jon
02a49c14b8 feat(itam): Change model to inherit from CenturionModel for Software model
ref: #823 #820
2025-06-12 11:42:03 +09:30
Jon
1f9061199c chore: add pytest mark for os version unit tests
ref: #823 #819
2025-06-12 11:25:53 +09:30
Jon
00b47e9d70 feat(itam): Add model OperatingSystemVersion to migrate for history and notes
ref: #823 closes #819
2025-06-12 11:22:36 +09:30
Jon
5aacecedcf refactor(itam): Update Test Suite for OperatingSystemVersion model
ref: #823 #819
2025-06-12 11:21:26 +09:30
Jon
316c86f54e feat(itam): Add Notes Serializer for OperatingSystemVersion model
ref: #823 #819
2025-06-12 11:20:47 +09:30
Jon
5dd76258ee feat(itam): Add AuditHistory Serializer for OperatingSystemVersion model
ref: #823 #819
2025-06-12 11:20:28 +09:30
Jon
d3039da85b feat(itam): Change model to inherit from CenturionModel for OperatingSystemVersion model
ref: #823 #819 closes #588
2025-06-12 11:19:26 +09:30
Jon
f79f934a0e chore(itam): add missing pytest mark serializer
ref:
2025-06-12 11:16:03 +09:30
Jon
efa3eeed5d Merge pull request #817 from nofusscomputing/model-inheritance 2025-06-11 21:17:47 +09:30
Jon
9f938a9a7e chore(itam): add missing import
ref:#817 #818
2025-06-11 20:44:00 +09:30
Jon
5d7ab6a740 feat(itam): Add model OperatingSystem to migrate for history and notes
ref:#817 closes #818
2025-06-11 20:12:04 +09:30
Jon
13884953cd refactor(itam): Update Test Suite for OperatingSystem model
ref:#817 #818
2025-06-11 20:11:17 +09:30
Jon
0b9ff0c779 refactor(itam): Update URL route name for DeviceSoftware model
ref:#817 #818
2025-06-11 20:08:18 +09:30
Jon
a0452ae2a6 feat(itam): Add Note Serializer for DeviceSoftware model
ref:#817 #818
2025-06-11 20:03:22 +09:30
Jon
9cf42cbcbd feat(itam): Add AuditHistory Serializer for DeviceSoftware model
ref:#817 #818
2025-06-11 20:00:34 +09:30
Jon
6c59d77267 feat(itam): Change model to inherit from CenturionModel for DeviceSoftware model
ref:#817 #818
2025-06-11 19:53:56 +09:30
Jon
5c6c2587a6 refactor(itam): Update Test Suite for DeviceSoftware model
ref:#817 closes #802
2025-06-11 19:13:54 +09:30
Jon
c15ef67813 feat(itam): Change model to inherit from Centurion for DeviceSoftware model
ref:#817 #802
2025-06-11 19:10:26 +09:30
Jon
2b4ebb31b3 refactor(itam): Update Test Suite for DeviceDeviceOperatingSystem model
ref:#817 closes #801
2025-06-11 18:40:39 +09:30
Jon
9c3541922f refactor(itam): Update URL route for DeviceDeviceOperatingSystem model
ref:#817 #801
2025-06-11 18:35:22 +09:30
Jon
cc3de16032 refactor(itam): Migration for updating model inheritance for DeviceDeviceOperatingSystem model
ref:#817 #801
2025-06-11 18:34:28 +09:30
Jon
febcf7a128 refactor(itam): Updated Unit model test suite for DeviceType model
ref:#817 closes #803
2025-06-11 17:13:02 +09:30
Jon
aabcf1bb23 feat(itam): Add model_tag to DeviceType model
ref:#817 #803 closes #581 #582
2025-06-11 17:12:24 +09:30
Jon
91fd05c3f9 refactor(devops): Updated Unit model test suite for DeviceModel model
ref:#817 closes #800
2025-06-11 17:00:05 +09:30
Jon
aa6a215926 refactor(devops): Migration for updating model inheritance for DeviceModel model
ref:#817 #800
2025-06-11 16:59:30 +09:30
Jon
68de6c0de2 refactor(itam): Updated Unit model test suite for Device model
ref:#817 closes #799
2025-06-11 16:03:39 +09:30
Jon
09caddccc8 feat(itam): Add DeviceType for history and notes data migration
ref:#817 #803
2025-06-11 15:57:46 +09:30
Jon
cfa2f81b0d feat(itam): Add DeviceModel for history and notes data migration
ref:#817 #800
2025-06-11 15:57:26 +09:30
Jon
d2b95eacda feat(itam): Add DEvice for history and notes data migration
ref:#817 #799
2025-06-11 15:57:04 +09:30
Jon
ec7bb51803 refactor(devops): Updated Unit model test ssuite for SoftwareEnabledFeatureFlag model
ref:#817 closes #812
2025-06-11 15:43:35 +09:30
Jon
59c590614c refactor(devops): Migration for updating model inheritance for SoftwareEnabledFeatureFlag model
ref:#817 #812
2025-06-11 15:42:31 +09:30
Jon
af84ae44ca refactor(devops): Update url route basename for SoftwareEnabledFeatureFlag model
ref:#817 #812
2025-06-11 15:41:57 +09:30
Jon
f9f33d9582 feat(devops): Switch SoftwareEnabledFeatureFlag model to inherit from CenturionModel
ref: #817 #812
2025-06-11 15:39:26 +09:30
Jon
152da48e15 Merge pull request #813 from nofusscomputing/model-inheritance 2025-06-10 14:36:59 +09:30
Jon
2d2c3092a6 chore: dont set model id for an instance during testing
ref: #813
2025-06-10 14:23:05 +09:30
Jon
ea2b642570 chore: correct pytest model marks
ref: #813
2025-06-10 14:01:32 +09:30
Jon
f75f2eae4d chore(core): correct test for get_url_kwargs for meta abstract model
ref: #813
2025-06-10 13:47:53 +09:30
Jon
425ec4373a fix(devops): Ensure mandatory fields are writeable for model GitRepository
ref: #813 #515
2025-06-10 13:37:56 +09:30
Jon
bc9c39ce95 refactor(tests): make all parameterized_ vars properties
ref: #813
2025-06-10 13:25:30 +09:30
Jon
854d6a0e37 chore(core): correct test for get_url_kwargs for meta abstract model
ref: #813 #793
2025-06-10 13:17:53 +09:30
Jon
e328ead070 chore(config_management): Mark get_url methods as xfail for abstract models
ref: #813 #793
2025-06-10 12:39:10 +09:30
Jon
7872b0b8f0 chore(config_management): ConfigGroupHost has no endpoint
ref: #813 #793
2025-06-10 12:34:42 +09:30
Jon
a75cd5a52e test(api): Update Functional API Permission test suite to cater for public RO endpoints
ref: #813 #767 #729
2025-06-10 12:14:29 +09:30
Jon
237778100b feat(devops): Update checkin model fixture so it creates the feature flag
ref: #813 #808
2025-06-10 12:13:19 +09:30
Jon
4fbad8bcbd feat(devops): Add methods get_url and get_url_kwargs to CheckIn model
ref: #813 #808
2025-06-10 12:12:08 +09:30
Jon
52bbf59ff5 chore: correct inheritance order
ref: #813
2025-06-10 10:09:10 +09:30
Jon
8088a827f4 test(core): Ensure model mehod get_url_kwargs returns a dict for all Centurion Models
ref: #813 closes #809 #810 #811
2025-06-10 09:01:30 +09:30
Jon
5a5989d5e2 feat(devops): Add migration to signal
ref: #813 #811
2025-06-10 08:53:19 +09:30
Jon
1c0baa6cad feat(devops): Add migration to signal
ref: #813 #810
2025-06-10 08:53:09 +09:30
Jon
39aa135c24 test(devops): Add GitLabRepository Unit Model test suite
ref: #813 #811
2025-06-10 08:51:48 +09:30
Jon
6856c54b42 Merge pull request #807 from nofusscomputing/model-inheritance 2025-06-09 17:18:52 +09:30
Jon
2ca99002c2 chore: update test db fixture
ref: #807
2025-06-09 17:07:49 +09:30
Jon
e0c6b66448 test(devops): Add GitHubRepository Unit Model test suite
ref: #807 #515 #810
2025-06-09 17:03:43 +09:30
Jon
3049c22dc0 test(devops): Add GitRepository Unit Model test suite
ref: #807 #515 #809
2025-06-09 17:03:05 +09:30
Jon
8eb9b6f6c4 feat(devops): Add migration to signal
ref: #807 #515 #809
2025-06-09 17:02:15 +09:30
Jon
4f3f9e9f4c feat(devops): Add migration to signal
ref: #807 #515 #810
2025-06-09 17:01:49 +09:30
Jon
cdf7b668e0 feat(devops): Update URL route basename
ref: #807 #515 #809 #810 #811
2025-06-09 17:00:12 +09:30
Jon
55dad89412 feat(devops): Migrations for switching GitLabRepository model to inherit from CenturionModel
ref: #807 #515 #811
2025-06-09 16:59:10 +09:30
Jon
db359a5267 feat(devops): Migrations for switching GitRepository model to inherit from CenturionModel
ref: #807 #515 #810
2025-06-09 16:58:55 +09:30
Jon
c29fdf1fc9 feat(devops): Migrations for switching GitRepository model to inherit from CenturionModel
ref: #807 #515 #809
2025-06-09 16:58:43 +09:30
Jon
bd77f211ac feat(devops): Serializers for GitRepository models notes and history
ref: #807 #515 #809
2025-06-09 16:58:17 +09:30
Jon
4498ecf13f feat(devops): Serializers for GitHubGitRepository models notes and history
ref: #807 #515 #810
2025-06-09 16:58:00 +09:30
Jon
1290dc81f2 feat(devops): Serializers for GitLabGitRepository models notes and history
ref: #807 #515 #809
2025-06-09 16:57:07 +09:30
Jon
fc14718bf2 feat(devops): Switch GitLabGitRepository model to inherit from CenturionModel
ref: #807 #515 #809
2025-06-09 16:56:20 +09:30
Jon
b462bccc0b feat(devops): Switch GitHubGitRepository model to inherit from CenturionModel
ref: #807 #515 #810
2025-06-09 16:56:02 +09:30
Jon
06ca4971d0 feat(devops): Switch GitRepository model to inherit from CenturionModel
ref: #807 #515 #809
2025-06-09 16:40:31 +09:30
Jon
7816fe0309 refactor(core): adjust CenturionSubModel to not be it's own inheritable class
it's not required as the inheritance does not match the actual class'.

ref: #807 #767
2025-06-09 16:39:07 +09:30
Jon
48b3982edc feat(devops): Update Checkin model url route basename
ref: #807 closes #808
2025-06-09 11:13:10 +09:30
Jon
106d77c396 feat(devops): Add app_namespace Checkin model
ref: #807 #808
2025-06-09 11:11:48 +09:30
Jon
79790dcda3 test(devops): Add Checkin Unit Model test suite
ref: #807 #676 #808
2025-06-09 11:07:39 +09:30
Jon
13fece3478 feat(devops): Add Checkin to migrate model history/notes
ref: #807 #808
2025-06-09 11:06:49 +09:30
Jon
d004413a6e feat(devops): Migrations for switching Checkin model to inherit from CenturionModel
ref: #807 #676 #808
2025-06-09 11:06:24 +09:30
Jon
dcff288283 feat(devops): Switch Checkin model to inherit from CenturionModel
ref: #807 #676 #808
2025-06-09 11:04:27 +09:30
Jon
69a6d720b0 test(devops): correct GitGroup Unit model test suite
ref: #807 closes #781
2025-06-09 10:19:10 +09:30
Jon
3a31e56a80 test(devops): correct FeatureFlag Unit model test suite
ref: #807 closes #782
2025-06-09 10:19:10 +09:30
Jon
dd99267eac feat(core): add TicketCommentCategory to history/notes migration
ref: #807 closes #798
2025-06-09 10:19:10 +09:30
Jon
6e70b255be test(core): Add TicketCommentCategory Unit model test suite
ref: #807 #798
2025-06-09 10:19:09 +09:30
Jon
83d8913757 feat(core): add model tag to ticket comment category
ref: #807 #798
2025-06-09 10:19:09 +09:30
Jon
3c10f6adda feat(core): Migrations for TicketCategory
ref: #807 closes #797
2025-06-09 10:19:09 +09:30
Jon
94f4463b7a feat(core): add TicketCategory to history/notes migration
ref: #807 closes #797
2025-06-09 10:19:09 +09:30
Jon
aca49d58a7 test(core): Add TicketCategory Unit model test suite
ref: #807 #797
2025-06-09 10:19:09 +09:30
Jon
69555f3526 feat(core): add model tag to ticket category
ref: #807 #797
2025-06-09 10:19:09 +09:30
Jon
a8494def17 feat(core): add Manufacturer to history/notes migration
ref: #807 closes #796
2025-06-09 10:19:09 +09:30
Jon
efda724e03 test(core): Add Manufacturer Unit model test suite
ref: #807 #796
2025-06-09 10:19:09 +09:30
Jon
7099f61112 feat(core): add model tag to manufacturer
ref: #807 #796
2025-06-09 10:19:09 +09:30
Jon
78326692a7 chore: add pytest marks to model tests
ref: #807
2025-06-09 10:19:09 +09:30
Jon
5d5d4b3217 feat(config_management): add ConfigGroups to history/notes migration
ref: #807 closes #795
2025-06-09 09:12:40 +09:30
Jon
36ef739e02 feat(config_management): add ConfigGroupSoftware to history/notes migration
ref: #807 closes #794
2025-06-09 09:09:15 +09:30
Jon
d30f0416fe feat(config_management): add ConfigGroupHosts to history/notes migration
ref: #807 closes #793
2025-06-09 09:02:38 +09:30
Jon
54ed000e34 feat(access): add tenant to history/notes migration
ref: #807 closes #790 #785 #786
2025-06-09 08:55:45 +09:30
Jon
f7f4f10815 test(access): Add Tenant Unit serializer test suite
ref: #807 #730 #790
2025-06-09 08:44:48 +09:30
Jon
b2de0747fd test: Add initial unit serializer test suite
ref: #807 #730
2025-06-09 08:43:55 +09:30
Jon
df5bac48b4 chore: Add action to mockview init
ref: #807
2025-06-09 08:37:24 +09:30
Jon
f4fa3a4da7 Merge pull request #805 from nofusscomputing/model-inheritance 2025-06-08 15:50:49 +09:30
Jon
b7a89ebd51 chore: remove unique field uuid from device kwargs
ref: #805
2025-06-08 15:32:11 +09:30
Jon
d62deb7034 fix(access): add property organization to Tenant model
required so that perms work

ref: #805 #790
2025-06-08 15:26:46 +09:30
Jon
b30a513d72 chore: Add pytest marks to tests
ref: #805
2025-06-08 15:13:52 +09:30
Jon
08707ee235 test(access): Update Tenant URL route basename again
ref: #805 #790
2025-06-08 14:48:36 +09:30
Jon
c547e1fa36 feat(access): Migration for switching model inheritence to CenturionModel
ref: #805 #799
2025-06-08 14:45:40 +09:30
Jon
e8b7b707d0 test(itam): Updated Unit model test for Device Model
ref: #805 #799
2025-06-08 14:45:24 +09:30
Jon
d68087ff02 feat(itam): Update model methods
ref: #805 #799
2025-06-08 14:44:57 +09:30
Jon
e1b450b537 chore: update test db fixture
ref: #805
2025-06-08 13:56:54 +09:30
Jon
50c9a70e8f feat(access): Migration for switching model inheritence to CenturionMixin
ref: #805 #790
2025-06-08 13:54:57 +09:30
Jon
1772d69a1e test(access): Update Tenant URL route basename
ref: #805 #790
2025-06-08 13:52:35 +09:30
Jon
fa36806233 test(access): Tenant Model Tests
ref: #805 #790
2025-06-08 13:51:45 +09:30
Jon
e782f1bdac chore(access): disable tests to be refactored
ref: #805 #790
2025-06-08 13:51:10 +09:30
Jon
1ecdfb9918 feat(access): Switch model inheritence to CenturionMixin
ref: #805 #790
2025-06-08 13:50:34 +09:30
Jon
dd4a0e631e test(api): Update Functional API Permissions to support listview models with kwargs
ref: #805 #767
2025-06-08 13:06:03 +09:30
Jon
e4d2826b91 chore: update test db fixture
ref: #805
2025-06-08 13:05:17 +09:30
Jon
2ed4fe0b9a test(api): exclude model ConfigGroupHosts from api permission tests as it has no endpoint
ref: #805
2025-06-08 12:39:28 +09:30
Jon
28a24a387e refactor(core): Move CenturionModel logic to Mixin
ref: #805 #767
2025-06-08 12:35:13 +09:30
Jon
d9815a4ba1 refactor(core): rename mixin -> mixins
ref: #805 #767
2025-06-08 12:23:30 +09:30
Jon
3acf962b08 Merge pull request #804 from nofusscomputing/model-inheritance 2025-06-07 22:45:30 +09:30
Jon
bdb370f499 chore(base): correct typo
ref: #804
2025-06-07 22:28:55 +09:30
Jon
05be8df1a3 chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #799
2025-06-07 22:13:09 +09:30
Jon
35d2d3672c chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-06-07 22:12:58 +09:30
Jon
b60e39869f chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #801
2025-06-07 22:12:41 +09:30
Jon
ab36f37eca chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #801
2025-06-07 22:06:36 +09:30
Jon
e80d305dfa chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-06-07 22:06:22 +09:30
Jon
d3671519f8 chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-06-07 22:01:24 +09:30
Jon
2c22da6f58 chore(base): correct typo
ref: #804
2025-06-07 21:52:22 +09:30
Jon
34402b46eb chore(base): Add pytest marks to pyproject.toml
ref: #804
2025-06-07 21:43:04 +09:30
Jon
e545aa4cb5 refactor(base): model instancxe code de-duplicated
ref: #804
2025-06-07 21:37:46 +09:30
Jon
35829cbeff feat(itam): Update url basename
ref: #804 #794
2025-06-07 21:34:57 +09:30
Jon
2c8ae8214d feat(itam): Update url basename
ref: #804 #795
2025-06-07 21:34:15 +09:30
Jon
16840a1212 feat(base): add support for manytomany for model unit tests
ref: #804 #799
2025-06-07 20:40:31 +09:30
Jon
be61435f67 feat(itam): Update url basename
ref: #804 #799
2025-06-07 20:28:09 +09:30
Jon
6fa37d70de chore(devops): disable pre CenturionModel inheritance tests
ref: #804 #782
2025-06-07 20:21:46 +09:30
Jon
89c483d89f revert(access): revert model inheriteance work (db)
ref: #804 #791 #792
2025-06-07 19:59:49 +09:30
Jon
b962004f07 revert(access): revert model inheriteance work
ref: #804 #791
2025-06-07 19:56:28 +09:30
Jon
1a7146f42c revert(access): revert model inheriteance work
ref: #804 #792
2025-06-07 19:55:52 +09:30
Jon
d8f58f9841 feat(core): Update url basename
ref: #804 #802
2025-06-07 19:39:49 +09:30
Jon
0e7d7bf5ff feat(core): Update url basename
ref: #804 #803
2025-06-07 19:30:57 +09:30
Jon
7af52a3cda feat(core): Update url basename
ref: #804 #798
2025-06-07 19:30:16 +09:30
Jon
e4aa07ac58 feat(core): Update url basename
ref: #804 #796
2025-06-07 19:28:50 +09:30
Jon
643a5f9307 feat(core): Update url basename
ref: #804 #800
2025-06-07 19:27:27 +09:30
Jon
964bbc6b0c feat(core): Update url basename
ref: #804 #797
2025-06-07 19:25:53 +09:30
Jon
e5e8820cab chore(device): disable pre CenturionModel inheritance tests
ref: #804 #803
2025-06-07 19:20:03 +09:30
Jon
13e7f3047a chore(device): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-06-07 19:19:50 +09:30
Jon
95b043eadc chore(device): disable pre CenturionModel inheritance tests
ref: #804 #801
2025-06-07 19:19:38 +09:30
Jon
7ac8a66072 chore(device): disable pre CenturionModel inheritance tests
ref: #804 #800
2025-06-07 19:19:26 +09:30
Jon
6bc3d9fa7c chore(device): disable pre CenturionModel inheritance tests
ref: #804 #799
2025-06-07 19:19:10 +09:30
Jon
339f39c2a1 chore(core): disable ticket tests for ticket models that are being depreciated
ref: #804 #564
2025-06-07 19:10:09 +09:30
Jon
811b3063b6 chore: update fixtures
ref: #804
2025-06-07 18:59:16 +09:30
Jon
516c4876ce fix(itam): Add missing import now
ref: #804 #799
2025-06-07 18:58:38 +09:30
Jon
1a06172247 test(api): API Permissions Functional test to supprt name as unique field
ref: #804
2025-06-07 18:58:08 +09:30
Jon
51fcc18f61 feat(access): TeamUsers do not require notes
ref: #804 #792
2025-06-07 18:57:24 +09:30
Jon
55f9c2cf17 chore: add test fixtures for models
ref: #804 #771
2025-06-07 18:29:43 +09:30
Jon
4ec2844bd5 chore: update test db fixture
ref: #804
2025-06-07 18:25:30 +09:30
Jon
a1b0aa3726 fix(core): notes meta model must add model_kwargs fixture
ref: #804 #768
2025-06-07 18:25:11 +09:30
Jon
616f0b35f9 fix(core): clean_fields for created_by field belongs in model that contains field
ref: #804 #768
2025-06-07 18:24:01 +09:30
Jon
a0c52bf149 feat(config_management): ConfigGroupHosts and ConfigGroupSoftware do not require notes
ref: #804 #793 #794
2025-06-07 17:49:09 +09:30
Jon
4888d64a29 fix(core): audit meta model must add model_kwargs fixture
ref: #804
2025-06-07 17:18:40 +09:30
Jon
7348c1a9aa fix: model fixture names must match model_name
ref: #804
2025-06-07 17:18:09 +09:30
Jon
1e8c37e4a2 chore(core): mock further attributes
ref: #804
2025-06-07 16:38:05 +09:30
Jon
1d840adaa7 chore(config_managment): disable pre CenturionModel inheritance tests
ref: #804 #794
2025-06-07 16:36:33 +09:30
Jon
407c295ead chore(config_managment): disable pre CenturionModel inheritance tests
ref: #804 #795
2025-06-07 16:36:24 +09:30
Jon
357a3ce53f fix: clean up mock model from django apps
ref: #804
2025-06-07 16:08:11 +09:30
Jon
b8a4834e7b chore(core): disable pre CenturionModel inheritance tests
ref: #804 #796 #797 #798
2025-06-07 15:46:28 +09:30
Jon
b20d8e35f0 test(config_management): Completed ConfigGroupSoftware Model Tests
ref: #804 #794
2025-06-07 15:43:19 +09:30
Jon
5328c72c37 test(config_management): Completed ConfigGroup Model Tests
ref: #804 #795
2025-06-07 15:42:56 +09:30
Jon
c244a26494 test(config_management): Completed ConfigGroupHost Model Tests
ref: #804 #793
2025-06-07 15:42:08 +09:30
Jon
ae980e4b1f chore: for mode instance, actually create it
ref: #804
2025-06-07 15:40:22 +09:30
Jon
2e7666c3c0 chore: add spt to class test cases for fields to be set as non-existant
ref: #804
2025-06-07 15:40:01 +09:30
Jon
ab3aec59ae refactor(config_management): Add ConfigGroupHost Model Tests
ref: #804 #793
2025-06-07 13:54:45 +09:30
Jon
e564f92166 refactor(config_management): Add ConfigGroupSoftware Model Tests
ref: #804 #794
2025-06-07 13:53:09 +09:30
Jon
6a82301e37 refactor(config_management): Add ConfigGroup Model Tests
ref: #804 #795
2025-06-07 13:52:35 +09:30
Jon
0c549310a5 chore(config_management): disable old ConfigGroupSoftware tests
ref: #804 #794
2025-06-07 13:51:44 +09:30
Jon
86899b666b chore(assistance): disable old ConfigGroup tests
ref: #804 #795
2025-06-07 13:48:38 +09:30
Jon
0cd65ee907 chore(assistance): correct fixuture kwargs for kb
ref: #804 #795
2025-06-07 13:48:24 +09:30
Jon
dda3c458d2 chore(access): remove tests for models that will be depreciated
ref: #804 #791 #792
2025-06-07 13:09:07 +09:30
Jon
f2b7263013 Merge pull request #789 from nofusscomputing/2025-06-06 2025-06-06 14:54:19 +09:30
Jon
36ab4ad212 fix(core): When obtaining model fields ensure it exists first
ref: #789 #767
2025-06-06 14:53:29 +09:30
Jon
da969330db chore(access): TeamUser model does not require audithistory
model will be depreciated soon

ref: #789 #792
2025-06-06 14:52:42 +09:30
Jon
7d8c71abcc chore: Update test db fixtures
ref: #789
2025-06-06 14:00:34 +09:30
Jon
3539d4368e chore(access): Team model Unit Tests for skip
ref: #789 #791
2025-06-06 13:51:31 +09:30
Jon
ab948187b2 feat(config_management): Add url_kwargs to ConfigGroupSoftware model
ref: #789 #794
2025-06-06 13:51:00 +09:30
Jon
0a3a94c2d8 feat(access): Add url_kwargs to Team model
ref: #789 #791
2025-06-06 13:50:28 +09:30
Jon
1bd3922253 feat(access): Add url_kwargs to TeamUser model
ref: #789 #792
2025-06-06 13:50:14 +09:30
Jon
7f93f624e2 chore: Remove all references to field is_global
field no longer used

ref: #789 #767
2025-06-06 13:38:59 +09:30
Jon
9c43b62880 feat(access): Update TeamUser API basename
ref: #789 #792
2025-06-06 13:27:17 +09:30
Jon
8f7b3769c5 feat(access): Update Team API basename
ref: #789 #791
2025-06-06 13:26:55 +09:30
Jon
129bdf3c52 fix(access): use getattr instead as attribute may exist as None
ref: #789
2025-06-06 13:25:48 +09:30
Jon
60fa41ab77 feat(itam): switch model Device to inheirt from CenturionModel
ref: #789 #799
2025-06-06 13:05:50 +09:30
Jon
6ea6a9190a feat(itam): switch model DeviceType to inheirt from CenturionModel
ref: #789 #803
2025-06-06 12:58:08 +09:30
Jon
e68c5e2cef feat(itam): switch model DeviceModel to inheirt from CenturionModel
ref: #789 #800
2025-06-06 12:54:19 +09:30
Jon
b6d777380d feat(core): switch model TicketCategory to inheirt from CenturionModel
ref: #789 #797
2025-06-06 12:40:33 +09:30
Jon
51203ae257 feat(core): switch model TicktetCommentCategory to inheirt from CenturionModel
ref: #789 #798
2025-06-06 12:39:41 +09:30
Jon
06c5600a09 feat(core): switch model Manufacturer to inheirt from CenturionModel
ref: #789 #796
2025-06-06 12:35:46 +09:30
Jon
38b1334f28 fix(assistance): make kb article field longer for model name
ref: #789
2025-06-06 12:21:52 +09:30
Jon
e26d653a6d feat(config_management): switch model ConfigGroupHosts to inheirt from CenturionModel
ref: #789 #795
2025-06-06 12:21:18 +09:30
Jon
950f9dd748 feat(config_management): switch model ConfigGroupSoftware to inheirt from CenturionModel
ref: #789 #795
2025-06-06 12:19:44 +09:30
Jon
a0f1368281 feat(config_management): switch model ConfigGroups to inheirt from CenturionModel
ref: #789 #795
2025-06-06 12:18:48 +09:30
Jon
5cf899175a feat(access): switch model TeamUsers to inheirt from CenturionModel
ref: #789 #792
2025-06-06 11:18:50 +09:30
Jon
b1155ee310 feat(access): switch model Team to inheirt from CenturionModel
ref: #789 #791
2025-06-06 11:18:41 +09:30
Jon
ee7996e1d4 test(core): mock the user object within the model context
ref: #789 #759
2025-06-06 07:00:07 +09:30
Jon
4caaa73d2b chore(access): remove old ui mixin
ref: #789 #757
2025-06-06 06:47:19 +09:30
Jon
cbde1f4e42 test(core): creating a model is a functional not unit test
ref: #789 #759
2025-06-06 06:46:53 +09:30
Jon
cee1f2bec1 chore: as part of api func tests, dont delete api perms user as part of fixture cleanup
ref: #789
2025-06-06 06:26:17 +09:30
Jon
bd0880744f chore: Add support for confftest for parameterized tests to have no values
ref: #789
2025-06-06 05:45:10 +09:30
Jon
0be181c34e docs: update release notes
ref: #789
2025-06-06 05:15:46 +09:30
Jon
9a83bf0fb4 chore(project_management): Remove API v1 code
ref: #789 #343
2025-06-06 05:13:27 +09:30
Jon
8f70c1ac7b chore(itam): Remove API v1 code
ref: #789 #343
2025-06-06 05:13:11 +09:30
Jon
68f79e5c90 chore(core): Remove API v1 code
ref: #789 #343
2025-06-06 05:13:03 +09:30
Jon
500b96dc79 chore(config_management): Remove API v1 code
ref: #789 #343
2025-06-06 05:12:27 +09:30
Jon
48c0b950cc chore(base): Remove API v1 code
ref: #789 #343
2025-06-06 05:12:18 +09:30
Jon
90991d403c chore(api): Remove API v1 code
ref: #789 #343
2025-06-06 05:12:10 +09:30
Jon
d7c1f571c6 chore(access): Remove API v1 code
ref: #789 #343
2025-06-06 05:12:00 +09:30
Jon
cf093ac708 feat(core): If user context not supplied, dont create audithistory for model
this allows tests and direct creation via code if required.

ref: #789 #759
2025-06-06 05:08:16 +09:30
Jon
d91011d83d Merge pull request #788 from nofusscomputing/2025-06-05 2025-06-06 04:09:29 +09:30
Jon
a1873aef9c chore(base): remove tests no longer used
ref: #788 #757
2025-06-05 17:00:31 +09:30
Jon
da1c691394 chore(base): remove methods no longer used in context processor
ref: #788 #757
2025-06-05 16:59:56 +09:30
Jon
a9071d624d chore: disable api field notess and history tests
tests being re-written

ref: #788 #759 #768
2025-06-05 16:23:54 +09:30
Jon
0b2567677a chore: Add premeta notes and history models to init so they can be migrated
ref: #788 #759 #768
2025-06-05 16:10:28 +09:30
Jon
087cd08a86 chore(core): remove old old history model
ref: #788 #759
2025-06-05 16:09:42 +09:30
Jon
f8575302e0 chore(core): Remove Template tags
not using django ui anymore

ref: #788 #757
2025-06-05 15:41:51 +09:30
Jon
2e72d2c595 chore(api): Remove pre-meta history code from common serializer
ref: #788 #759
2025-06-05 15:41:13 +09:30
Jon
aad95797a8 chore(settings): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:40:29 +09:30
Jon
afbdf8309c chore(project_management): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:40:17 +09:30
Jon
9addfc3abc chore(itim): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:39:56 +09:30
Jon
d786b713f6 chore(itam): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:39:43 +09:30
Jon
4302a913e4 chore(devops): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:39:27 +09:30
Jon
8298c18ad0 chore(core): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:39:06 +09:30
Jon
05fa018de8 chore(config_management): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:38:12 +09:30
Jon
da7638c196 chore(api): Remove pre-meta notes code from common serializer
ref: #788 #768
2025-06-05 15:37:42 +09:30
Jon
a1c8624c22 chore(accounting): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:37:00 +09:30
Jon
5df9f35b87 chore(access): Remove pre-meta notes code
ref: #788 #768
2025-06-05 15:36:48 +09:30
Jon
5aee3d495e chore: temp disable api field notes and history checks
ref: #788
2025-06-05 15:32:39 +09:30
Jon
0dd4f90b79 chore(base): pass instance of model for class tests
ref: #788
2025-06-05 14:58:54 +09:30
Jon
9f58eb60e5 chore(project_management): disable project test that contain kb untill rewrite
ref: #788 #735
2025-06-05 14:44:00 +09:30
Jon
d193f6661e feat(access): Add init to tenancy model to clear state
ref: #788 #767
2025-06-05 14:35:14 +09:30
Jon
9fe0324118 chore(core): disable ticket linked item kb article functional test
ref: #788 #735
2025-06-05 14:28:55 +09:30
Jon
420f8eedc0 chore(settings): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:11:27 +09:30
Jon
56e94c814a chore(project_management): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:11:18 +09:30
Jon
03d0d2565c chore(itim): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:11:08 +09:30
Jon
aa9ac74886 chore(itam): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:10:40 +09:30
Jon
d55f322bd7 chore(core): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:10:23 +09:30
Jon
3819839893 chore(config_management): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:10:05 +09:30
Jon
1fe0bbdf33 chore(base): disable tests that require rewrite
ref: #788 #735
2025-06-05 14:09:55 +09:30
Jon
27b48f7f30 chore(assistance): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:09:41 +09:30
Jon
513bc6223b chore(api): Remove old Notes Tests
ref: #783 #735 #759
2025-06-05 14:09:28 +09:30
Jon
978e5d3e56 chore(access): Remove no longer used Django UI
ref: #788 #757
2025-06-05 14:08:52 +09:30
Jon
69e8402f89 chore(base): Dont reload model, reset the parts that change in tests
creates issues with mocking

ref: #788
2025-06-05 14:08:04 +09:30
Jon
a5df8f0ff8 chore(core): disable old delete so it's no longer used by models yet to inherit from CenturionModel
ref: #788 #759
2025-06-05 14:06:27 +09:30
Jon
808e9b25ad feat(core): Ensure that model has user context
ref: #788 #759
2025-06-05 13:23:05 +09:30
Jon
9b5acf65e8 chore(core): disable old delete so it's no longer used by models yet to inherit from CenturionModel
ref: #788 #759
2025-06-05 13:21:58 +09:30
Jon
fc7620aa63 chore(itam): mock base delete so abstract classes can be called
ref: #788
2025-06-05 13:20:53 +09:30
Jon
be465fcac6 chore(assistance): disable tests that require rewrite
ref: #788 #735
2025-06-05 11:55:53 +09:30
Jon
5d18ed991b chore(base): add fixture try block
ref: #788
2025-06-05 09:51:51 +09:30
Jon
738e6a9322 Merge pull request #783 from nofusscomputing/2025-06-02 2025-06-04 14:25:53 +09:30
Jon
f7fcbb38d8 chore(base): fixture sanitization
ref: #783
2025-06-04 14:23:13 +09:30
Jon
794da69567 chore(itam): Remove more old Notes Tests
ref: #783 #735 #759
2025-06-04 13:41:23 +09:30
Jon
89bb082b61 chore(settings): Remove old Notes Tests
ref: #783 #735 #759
2025-06-04 13:38:45 +09:30
Jon
7e4f705e1e chore(project_management): Remove old Notes Tests
ref: #783 #735 #759
2025-06-04 13:38:38 +09:30
Jon
bc7f50b958 chore(itim): Remove old Notes Tests
ref: #783 #735 #759
2025-06-04 13:38:28 +09:30
Jon
8a4879f1f2 chore(itam): Remove old Notes Tests
ref: #783 #735 #759
2025-06-04 13:38:22 +09:30
Jon
2787e7b67c chore(core): Remove old Notes Tests
ref: #783 #735 #759
2025-06-04 13:36:23 +09:30
Jon
455e2e4139 chore(config_management): Remove old Notes Tests
ref: #783 #735 #759
2025-06-04 13:36:16 +09:30
Jon
cad76581ad chore(access): Remove old Notes Tests
ref: #783 #735 #759
2025-06-04 13:36:05 +09:30
Jon
93242a3710 chore(settings): fixture sanitization
ref: #783
2025-06-04 13:27:58 +09:30
Jon
40cddc2283 chore(settings): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:30:29 +09:30
Jon
ea55873478 chore(project_management): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:30:22 +09:30
Jon
3ce7bbbcbf chore(itim): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:30:12 +09:30
Jon
46fb0e08e2 chore(itam): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:30:04 +09:30
Jon
1023e516c9 chore(human_resources): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:29:56 +09:30
Jon
c79cb8ec68 chore(core): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:29:43 +09:30
Jon
264227c6a6 chore(config_management): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:29:36 +09:30
Jon
dd869977e6 chore(accounting): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:29:27 +09:30
Jon
b67609336a chore(access): Remove old History Tests
ref: #783 #735 #759
2025-06-04 11:29:18 +09:30
Jon
65cf0873ca feat(core): Add supprt to model_instance fixture for manytomany field
ref: #783
2025-06-04 10:46:57 +09:30
Jon
0297ea3406 fix(assistance): Add missing field model_notes to KB serializer
ref: #783 #785
2025-06-04 10:46:32 +09:30
Jon
6dc4d493f9 chore(assistance): Remove old KB notes
ref: #783 #785
2025-06-04 10:46:04 +09:30
Jon
bbddb42fab chore(assistance): Remove old KB Category notes
ref: #783 #786
2025-06-04 10:45:56 +09:30
Jon
c4d36accd6 chore(base): Update test db creation fixtures
ref: #783
2025-06-04 10:02:22 +09:30
Jon
a639bcfdeb refactor(assistance): Refactor KnowledgeBaseCategory Unit model tests
ref: #783 #786 #735
2025-06-04 09:59:20 +09:30
Jon
14968bdfcc refactor(assistance): Update KnowledgeBase Unit viewset url basename
ref: #783 #785 #735
2025-06-04 09:58:31 +09:30
Jon
d0e39f4bb1 refactor(assistance): Refactor KnowledgeBase Unit model tests
ref: #783 #785 #735
2025-06-04 09:45:50 +09:30
Jon
9e506a23c4 feat(core): Add supprt to model create test for manytomany field
ref: #783
2025-06-04 09:42:04 +09:30
Jon
8c7fbc31b6 chore(assistance): Remove KnowledgeBase Note ViewSet
ref: #783 #785
2025-06-04 09:09:30 +09:30
Jon
13353c761e chore(assistance): Remove KnowledgeBaseCategory Note ViewSet
ref: #783 #786
2025-06-04 09:09:06 +09:30
Jon
c97f05e5a5 chore(assistance): TMP disable KB category tests that require refactor
ref: #783 #786
2025-06-04 09:08:19 +09:30
Jon
0a8dc74db5 chore(assistance): TMP disable KB tests that require refactor
ref: #783 #785
2025-06-04 09:07:33 +09:30
Jon
d651a07be7 feat(assistance): migrations for new history and notes models for KnowledgeBaseCategory model
ref: #783 #786
2025-06-04 07:50:35 +09:30
Jon
11ebb94fd7 feat(assistance): migrations for new history and notes models for KnowledgeBase model
ref: #783 #785
2025-06-04 07:49:49 +09:30
Jon
ccd4f5caa4 feat(assistance): Model inheritance migrations
ref: #783 #785 #786
2025-06-04 07:49:16 +09:30
Jon
c7479168bd chore(assistance): remove old history and notes tests for KnowledgeBaseCategory model
ref: #783 #786
2025-06-04 07:47:50 +09:30
Jon
2841ff1374 chore(assistance): remove old history and notes tests for KnowledgeBase model
ref: #783 #785
2025-06-04 07:47:31 +09:30
Jon
a1392a97f0 refactor(assistance): Add new history and notes Serializer for KnowledgeBase model
ref: #783 #785
2025-06-04 07:46:58 +09:30
Jon
9928a18220 refactor(assistance): Add new history and notes Serializer for KnowledgeBaseCategory model
ref: #783 #786
2025-06-04 07:46:09 +09:30
Jon
da7d1d9295 refactor(assistance): Change KnowledgeBaseCategory model inheritance TenancyObject -> CenturionModel
ref: #783 #786
2025-06-04 07:45:34 +09:30
Jon
0db90d60cd refactor(assistance): Change KnowledgeBase model inheritance TenancyObject -> CenturionModel
ref: #783 #785
2025-06-04 07:44:06 +09:30
Jon
600c05658c refactor(assistance): MV kb category model to its own file
ref: #783
2025-06-04 07:15:26 +09:30
Jon
a82a417977 chore(devops): Remove GitGroup old notes URL route
ref: #783 #781
2025-06-04 07:03:18 +09:30
Jon
c8187e90a1 chore(devops): Remove GitGroup old notes serializer
ref: #783 #781
2025-06-04 07:01:44 +09:30
Jon
a339f55214 chore(devops): Remove GitGroup old notes viewset
ref: #783 #781
2025-06-04 07:01:26 +09:30
Jon
719b02dd2b chore(devops): Add FeatureFlag old history and notes models to init so they exist
ref: #783 #782
2025-06-03 10:28:10 +09:30
Jon
d4d4305852 chore(devops): Add GitGroup old history and notes models to init so they exist
ref: #783 #781
2025-06-03 10:27:33 +09:30
Jon
1acae25889 feat(core): Migrate Centurion Model history and notes within a post_migrate signal
ref: #783 #758 #759 #778 #768
2025-06-03 10:26:58 +09:30
Jon
d6fd8fd569 fix(core): Before attempting to get model audit data confirm fields dont already exist
ref: #783
2025-06-03 10:25:53 +09:30
Jon
269873972c fix(api): check if model has notes enabled before adding url to body
ref:
2025-06-02 09:43:43 +09:30
Jon
c2dd015264 test(devops): re-implement temp removed test suites.
ref: #780 #515 #782
2025-06-02 09:42:54 +09:30
Jon
a9c698b531 test(api): API Permissions Auto-Creator test suite
ref: #780 #730 #767
2025-06-02 09:42:54 +09:30
Jon
d9ef3c2535 test(devops): Add GitGroup API Permissions tests
ref: #780 #515
2025-06-02 09:42:54 +09:30
Jon
8b77ee1078 test(core): Add fixtures for api permission tests
ref: #780 #730
2025-06-02 09:42:54 +09:30
Jon
e6aa8cb69b test(core): rewrite api permissions test suite to use pytest and fixtures
ref: #780 #730
2025-06-02 09:42:54 +09:30
Jon
392c2ee284 feat(core): Add ability to CenturionModel get_url to be either detail/list
ref: #780 #767
2025-06-02 09:42:54 +09:30
Jon
c741972173 fix(api): Only return View Serialized data if status code is HTTP/2xx
ref: #780
2025-06-02 09:42:54 +09:30
Jon
334fd7bc8a chore: update db test fixture
ref: #780
2025-06-02 09:42:54 +09:30
Jon
46cb7ab752 refactor(tests): Create global model fixtures
ref: #780 #729
2025-06-02 09:42:54 +09:30
Jon
e7350bcd03 chore(tests): Depreciate old FeateFlag Tests
ref: #780 #729
2025-06-02 09:42:54 +09:30
Jon
eada5f1e27 docs(development): Update test suit locations
ref: #780 #729
2025-06-02 09:42:54 +09:30
Jon
80bac75426 chore: move api render to functional dir
ref: #780 #729
2025-06-02 09:42:54 +09:30
Jon
8f9db90fb7 chore(devops): Remove FeatureFlag Notes Tests
ref: #780 #782
2025-06-02 09:42:54 +09:30
Jon
7fbff96600 chore(devops): Remove FeatureFlag History Tests
ref: #780 #782
2025-06-02 09:42:54 +09:30
Jon
baa810cb0c chore(devops): Remove FeatureFlag notes URLRoute
ref: #780 #782
2025-06-02 09:42:54 +09:30
Jon
c452117173 chore(devops): Remove FeatureFlag notes ViewSet
ref: #780 #782
2025-06-02 09:42:54 +09:30
Jon
61b44614b6 chore(devops): Remove FeatureFlag notes Serializer
ref: #780 #782
2025-06-02 09:42:54 +09:30
Jon
d396cc7c14 feat(core): New Management command to list models
ref: #780 #771
2025-06-02 09:42:54 +09:30
Jon
5ab9d51c99 refactor(devops): Switch FeatureFlag model unit tests to CenturionModel
ref: #780 #782
2025-06-02 09:42:54 +09:30
Jon
b82907ecb5 feat(devops): Switch model FeatureFlag inheritance to CenturionModel
ref: #780 #782
2025-06-02 09:42:54 +09:30
Jon
5eb11a0ee3 docs: Update model notes docs for new meta model
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
a0d8008215 test(core): Ensure Method clean_fields functions for CenturionNotesModel
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
33c9d475a1 test(core): Function Model test suite for CenturionModelNote Meta Models
ref: #779 #778 #768
2025-06-02 09:42:54 +09:30
Jon
ce998fe5aa chore: Update test fixtures
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
70888ab701 test(core): Interim Unit Model test suite for CenturionModelNote Meta Models
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
00558eefde test(core): Interim Unit Model test suite for CenturionModelNote
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
3770faf0a3 feat(core): Disable Notes for model CenturionModelNote
A notes model does not require notes

ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
1930cd302e feat(devops): Enable Model notes for GitGroup
ref: #779 #778 #515
2025-06-02 09:42:54 +09:30
Jon
0d8ca91943 feat(core): add Swagger docs for CenturionModelNotes ViewSet
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
6d5ff86d5c feat(core): Meta Model for CenturionModelNotes
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
c6cafd1bbb feat(core): Finalize Serializer for CenturionModelNotes
ref: #779 #778
2025-06-02 09:42:54 +09:30
Jon
0bedff4bfe feat(api): Add to common serializer meta notes model for notes url
ref: #779 #767
2025-06-02 09:42:54 +09:30
Jon
2be03622e8 feat(core): Interim Meta model CenturionNotes
ref: #776 #778
2025-06-02 09:42:53 +09:30
Jon
36b2a93c2f feat(core): Interim ViewSet for model CenturionNotes
ref: #776 #778
2025-06-02 09:42:53 +09:30
Jon
0d292dcddc feat(core): URL Route for model CenturionNotes
ref: #776 #778
2025-06-02 09:42:53 +09:30
Jon
c2d8cf8de6 feat(core): Serializer for model CenturionNotes
ref: #776 #778
2025-06-02 09:42:53 +09:30
Jon
1ace682088 feat(core): Migration for model CenturionNotes
ref: #776 #778
2025-06-02 09:42:53 +09:30
Jon
49dec22e98 feat(core): Add model CenturionNotes
ref: #776 #778
2025-06-02 09:42:53 +09:30
Jon
332cca361a chore: correct liniting errors
ref: #776
2025-06-02 09:42:53 +09:30
Jon
3cc6b06762 feat(devops): dont allow deleting a git group if it has children
ref: #776
2025-06-02 09:42:53 +09:30
Jon
67701a99ba chore: correct liniting errors
ref: #776
2025-06-02 09:42:53 +09:30
Jon
2fd4ffe8e7 chore: update pylint to v3.3.7
ref: #776
2025-06-02 09:42:53 +09:30
Jon
a970fa4eb2 refactor(settings): move url routes from core.urls to own module urls_api.py
ref: #776 closes #777
2025-06-02 09:42:53 +09:30
Jon
d4e8f99993 refactor(project_management): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-06-02 09:42:53 +09:30
Jon
e74f30fd7b refactor(itim): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-06-02 09:42:53 +09:30
Jon
c0ee9f1c51 refactor(itam): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-06-02 09:42:53 +09:30
Jon
fa1b90b90c refactor(core): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-06-02 09:42:53 +09:30
Jon
bbfbbae98d refactor(config_management): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-06-02 09:42:53 +09:30
Jon
a53ba4eb50 refactor(assistance): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-06-02 09:42:53 +09:30
Jon
cadc934b94 refactor(access): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-06-02 09:42:53 +09:30
Jon
7e6f7a9e76 chore: add fixture to setup user perms for api tests
ref: #776 #730 #735
2025-06-02 09:42:53 +09:30
Jon
488e60b90c docs(development): Audit History API Docs added
ref: #776 #759
2025-06-02 09:42:53 +09:30
Jon
ec04f601be docs(development): Audit History updates
ref: #776 #759
2025-06-02 09:42:53 +09:30
Jon
920801c82b chore: rm pylint error
this file is due for refactor so urls are in own modules

ref: #776
2025-06-02 09:42:53 +09:30
Jon
5742a0cb35 chore: code review fixes
ref: #775
2025-06-02 09:42:53 +09:30
Jon
4374193573 chore: add further pylint rules
ref: #775
2025-06-02 09:42:53 +09:30
Jon
c9cdd1d69c test(core): Dynamic Unit Test Suites for Meta Models AuditHistory
ref: #775 #759 #767
2025-06-02 09:42:53 +09:30
Jon
51ba4b1631 test(core): Unit Test Centurion Model method __str__
ref: #767
2025-06-02 09:42:53 +09:30
Jon
9fc4d46569 test(core): Unit Test Centurion Model method get_url_kwargs
ref: #775 #759 #767
2025-06-02 09:42:53 +09:30
Jon
6c32eeb73e test(core): Unite Tesxt Centurion Model method get_url attr _is_submodel set
ref: #775 #759 #767
2025-06-02 09:42:53 +09:30
Jon
6f5c50ac06 test(core): Unite Tesxt Centurion Model method get_url attr model_name set
ref: #775 #767
2025-06-02 09:42:53 +09:30
Jon
7b9cde6b76 feat(devops): Add model tag attribute to model
ref: #773 #515 #576
2025-06-02 09:42:53 +09:30
Jon
a02c45861f feat(core): Add to Centurion Model an attribute to set the models tag
ref: #773 #767 #576
2025-06-02 09:42:53 +09:30
Jon
b3d1045c57 docs(user): Add AuditHsitory Docs
ref: #773 #759
2025-06-02 09:42:53 +09:30
Jon
0a7dcbb0ed feat(core): Add Context to model when ViewSet loads
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
1a9d94e3ee feat(devops): Add AuditHistory Serializer for GitGroup
ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
16c39438d9 feat(core): Add AuditHistory Serializer
ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
640c145197 feat(core): Add AuditHistory ViewSet
ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
b6c721a089 feat(core): Add URL route for AuditHistory
ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
9f5b199557 feat(core): Add audithistory URL to serializer for models with _audit_enabled=True
ref: #773 #767 #759
2025-06-02 09:42:52 +09:30
Jon
371ba25f93 docs(development): Initial Serializer docs
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
3b3587d596 fix(core): Conduct kwargs check fr ticket comment serializer during init
ref: #773
2025-06-02 09:42:52 +09:30
Jon
3ee61c2441 refactor(api): Update Common ViewSet methds for re-write
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
48d826ec06 feat(core): Models url kwarg helper
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
1fef7de91e feat(core): Support setting custom model name for url basename
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
631a430a2a feat(api): Add sub-model filter to get_queryset method
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
7b41f35d37 chore: correct test so it works
ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
1866738763 test(core): Ensure model that has audit enableed has audit model
ref: #773 #515
2025-06-02 09:42:52 +09:30
Jon
48d7f7f7b2 test(core): Add Functional model Test Suite for CenturionAuditModel
ref: #773 #515
2025-06-02 09:42:52 +09:30
Jon
8aece83b36 test(devops): Ensure that a Github group cant have a parent/"be nested"
ref: #773 #515 #249
2025-06-02 09:42:52 +09:30
Jon
6e3224b60f test(devops): Ensure that when create a child git group that the tenancy matches the parent git group
ref: #773 #515
2025-06-02 09:42:52 +09:30
Jon
1e521c04f6 test(devops): Add Functional model Test Suite
ref: #773 #515
2025-06-02 09:42:52 +09:30
Jon
f4aa19990f test(core): Add Base Centurion model Functional Test Suite
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
b50923915f test(access): Add Base Tenancy model Functional Test Suite
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
bb13547140 test(base): Add Base model Functional Test Suite
ref: #773 #767
2025-06-02 09:42:52 +09:30
Jon
91561c6857 feat(core): Disable models audit history on model delete
can't create an audit entry without a model, why? it was deleted

ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
027c9e977e fix(core): Enable CenturionAudit model to get model history for item being deleted
ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
45ee804278 chore: update test fixtures
ref: #773
2025-06-02 09:42:52 +09:30
Jon
62bc8f93a1 test(core): Model Unit Tests for AuditHistory get_model_history method
ref: #773 #759
2025-06-02 09:42:52 +09:30
Jon
ca1171c0f7 chore: add pytest marks to tests
ref: #772
2025-06-02 09:42:51 +09:30
Jon
1dcd24950d test(core): reset vals so as not to fuck other tests over
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
10f776aa72 test(core): Correct test for method get_audit_values for CenturionAbstractModel
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
917a0c23cd chore(core): update Current ModelHistory query to exclude new AuditHistory model
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
eff00ca518 chore(devops): remove tests that were left there when file was copied from another test suite
ref: #772 #515
2025-06-02 09:42:51 +09:30
Jon
1214e78bc6 chore: adjust test so params are under a property
ref: #772
2025-06-02 09:42:51 +09:30
Jon
ce8e3575c3 fix(core): When creating the AuditHistory entry for a model, use the user from context
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
94180e2063 fix(core): When collecting AuditHistory cater for models being created
ref: #772 #767
2025-06-02 09:42:51 +09:30
Jon
f3681897fa feat(core): Use Previous TenancyManager until UserModel rewrite done
ref: #772 #766 #767
2025-06-02 09:42:51 +09:30
Jon
e607153027 chore: remove Adding of history tasks from issue_template for new models
ref: #772
2025-06-02 09:42:51 +09:30
Jon
9f60ad7fca fix(api): remove surerflous feature for fetching app_namespace for models metadata
if the model required the parent models `app_namespace` it already exists when the class is inerited,

ref: #772
2025-06-02 09:42:51 +09:30
Jon
e6dd88f9d4 test(devops): Initial Model Unit tests for GitGroup
ref: #772 #515
2025-06-02 09:42:51 +09:30
Jon
12a11e8e81 refactor(devops): Switch GitGroup Model to CenturionModel
ref: #772 #515 #759 #767
2025-06-02 09:42:51 +09:30
Jon
c40076c0ea feat(core): Process a models history within AuditHistory
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
11081e444f feat(core): Enable AuditHistory signal to start when apps are ready
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
55642823d3 feat(core): Add model instance to history object during history creation
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
6a2687d5b8 feat(core): Update Meta AuditModel db_name to be suffixed _audithistory
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
351f577104 fix(core): Correct attribute names for referencing a Centurion Model from an AuditModel
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
590783a5a1 test(core): Add field model_notes as an excluded field for AuditModels
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
829e5324f3 feat(core): remove unnessecary method clean_fields from audit model
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
4fb4044168 chore(core): Add temp objects to CenturionAbstractModel to support depreciated features
these will be removed when #767 is complete

ref: #772 #767
2025-06-02 09:42:51 +09:30
Jon
b8a732b314 fix(core): Correct before lookup for current models audit history
ref: #772 #759 #767
2025-06-02 09:42:51 +09:30
Jon
724af5fb5c feat(core): remove un-needed field model_notes from audit models
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
0e1b87e15e docs(development): Add new abstract models to API docs
ref: #772
2025-06-02 09:42:51 +09:30
Jon
aff770657f test(core): Remaining Unit Model Test Cases for CenturionAuditMeta Model
ref: #772 #759
2025-06-02 09:42:51 +09:30
Jon
c6dd701e8c docs(development): Partial update to models
ref: #772 #767
2025-06-02 09:42:50 +09:30
Jon
2dea4ea043 refactor(core): Loading of meta models should not be hidden behind program start ags
Meta Models must always exist

ref: #772 #759
2025-06-02 09:42:50 +09:30
Jon
d79dbc15a2 test(core): Initial Unit Model Test Cases for CenturionAuditMeta Model
ref: #772 #759
2025-06-02 09:42:50 +09:30
Jon
b79dd3e3bf test(core): Unit Model Test Cases for CenturionSubAbstract model
ref: #772 #767
2025-06-02 09:42:50 +09:30
Jon
406e579921 test(core): Initial Unit Model Test Cases for CenturionAudit Model
ref: #772 #759
2025-06-02 09:42:50 +09:30
Jon
1e16718d9d test(core): Unit test cases for Centurion get_url relative + non-relative
ref: #772 #767
2025-06-02 09:42:50 +09:30
Jon
eddd610613 test(access): Unit Model Tests for TenancyAbstractModel
ref: #770 #767 #445
2025-06-02 09:42:50 +09:30
Jon
3c73c272cb chore(core): remove methods not required
ref: #770 #767
2025-06-02 09:42:50 +09:30
Jon
a41a30e11e refactor(core): To obtain audit_values loop through model fields
ref: #770 #767
2025-06-02 09:42:50 +09:30
Jon
53fc305fa3 fix(core): When deleting a model check if sub-model within delete method
ref: #770 #767
2025-06-02 09:42:50 +09:30
Jon
1c77b69745 test(base): Unit Common Model test cases suite
ref: #770 #758 #767
2025-06-02 09:42:50 +09:30
Jon
bb8474d200 test(base): Unit Common Class test cases suite
ref: #770 #758 #767
2025-06-02 09:42:50 +09:30
Jon
48b77855b1 test(access): Unit Model Tests for TenancyAbstractModel
ref: #770 #758 #767
2025-06-02 09:42:50 +09:30
Jon
909e5b5bf2 chore(pytest): rename pytest_configure -> pytest_report_header
ref: #770
2025-06-02 09:42:50 +09:30
Jon
3dc65adcf2 chore(pytest): Create and use fixture of Fresh DB instead of migrations for testing
ref: #770
2025-06-02 09:42:50 +09:30
Jon
a0b99eae3b chore(pytest): give parameterized tests the option to use functions as a value
ref: #770
2025-06-02 09:42:50 +09:30
Jon
307e4ce614 feat(core): Run meta models create on Core module ready
ref: #770 #759
2025-06-02 09:42:50 +09:30
Jon
4fd5ca5017 feat(core): New model core.CenturionAudit
ref: #770 #759
2025-06-02 09:42:50 +09:30
Jon
7aa24c3ec1 feat(core): cause sub-audit models to chuck a wobbler if clean_fields not re-implementated
ref: #770 #759
2025-06-02 09:42:50 +09:30
Jon
c2ad95f927 chore: remove unused imports
ref: #770
2025-06-02 09:42:50 +09:30
Jon
06f3839569 chore: comment out test setup for skeleton test suites not in use yet
ref: #770
2025-06-02 09:42:50 +09:30
Jon
f975e84611 refactor: rejig whats in each inherited centurion model
ref: #770
2025-06-02 09:42:50 +09:30
Jon
81f7c50877 chore: add linting rule for unused imports
ref: #770
2025-06-02 09:42:50 +09:30
Jon
2010f461f9 fix(access): Tenancy Manager should not attempt to get org as related field if it does not exist
ref: #770
2025-06-02 09:42:50 +09:30
Jon
7335daf806 feat(access): remove mill-seconds from datetime auto fields
ref: #770 #759
2025-06-02 09:42:50 +09:30
Jon
17f636789b chore: correct code from revert
ref: #765
2025-06-02 09:42:50 +09:30
Jon
446949ce5b feat(core): Centurion model Base
ref: #765 #767
2025-06-02 09:42:50 +09:30
Jon
d1c77a0bb5 feat(core): Centurion Audit model
ref: #765 #759
2025-06-02 09:42:50 +09:30
Jon
da11de47dc fix(api): ensure val returns at least none
ref: #765
2025-06-02 09:42:50 +09:30
Jon
b4d14cc1c0 chore(access): planning work for centurion Tenancy model
ref: #765 #766
2025-06-02 09:42:50 +09:30
Jon
c15a37b7b6 chore(access): planning work for centurion user model
ref: #765 #766
2025-06-02 09:42:50 +09:30
Jon
dc83f60f2c revert(core): Relocate history model class
reverted commit was 5cc08e3e94

ref: #765 #766
2025-06-02 09:42:50 +09:30
Jon
7251b60389 refactor(access): prefetch org with tenancy object
ref: #765
2025-06-02 09:42:49 +09:30
Jon
4b97245f27 chore(core): remove old manual migration no longer required
ref: #765
2025-06-02 09:42:49 +09:30
Jon
eea6e266e3 feat(core): permissions getter for role model
ref: #765 #551
2025-06-02 09:42:49 +09:30
Jon
27e2f3d664 chore(core): skeleton test cases
ref: #765 #759
2025-06-02 09:42:49 +09:30
Jon
0f5720c715 feat(core): Audit History Signal for Delete/Save
ref: #765 #759
2025-06-02 09:42:49 +09:30
Jon
2f7d7e9db2 chore: Add pylint conf
ref: #765
2025-06-02 09:42:49 +09:30
Jon
8227fe5de0 chore: correct pylint errors
ref: #765
2025-06-02 09:42:49 +09:30
Jon
ed49ee6a10 feat(core): Dynamic History model creation
ref: #765 #758 #759
2025-06-02 09:42:49 +09:30
Jon
db9af1f232 refactor(core): Relocate history model class
ref: #765 #759
2025-06-02 09:42:49 +09:30
Jon
5e095b9deb refactor(base): rename app to centurion
ref: #764
2025-06-02 09:42:49 +09:30
4fb94bdd6d build: bump version 1.17.0 -> 1.17.1 2025-06-02 00:05:20 +00:00
Jon
e2582373ad fix(base): Add python metrics to prometheus exporter
ref: #436
2025-06-02 08:25:37 +09:30
309483ea08 build: bump version 1.16.0 -> 1.17.0 2025-05-16 10:31:55 +00:00
Jon
b2bc200eb1 Merge pull request #740 from nofusscomputing/feature-next-release 2025-05-16 19:46:31 +09:30
Jon
a0ab0deb2a chore: squash migrations to reduce amount
ref: #740
2025-05-16 19:29:02 +09:30
Jon
1f9491ce73 Merge pull request #761 from nofusscomputing/new-model-access-organization 2025-05-16 03:26:34 +09:30
Jon
1663f19b2a refactor(human_resources): Update Functional ViewSet to use PyTest for Employee Model
ref: #761 #730
2025-05-16 03:10:00 +09:30
Jon
07277862cf refactor(Access): Update Functional ViewSet to use PyTest for Person Model
ref: #761 #730
2025-05-16 03:09:49 +09:30
Jon
f1016bd9cc refactor(Access): Update Functional ViewSet to use PyTest for Entity Model
ref: #761 #730
2025-05-16 03:07:57 +09:30
Jon
c39f479b96 refactor(Access): Update Functional ViewSet to use PyTest for Contact Model
ref: #761 #730
2025-05-16 03:07:46 +09:30
Jon
a4d1a2bf76 test(access): Functional ViewSet Test Suite Company model
ref: #761 #760
2025-05-16 03:07:28 +09:30
Jon
8bba04305f refactor(Access): Update Functional Permission to use PyTest for Person Model
ref: #761 #730
2025-05-16 02:18:00 +09:30
Jon
516d6fc136 refactor(Access): Update Functional Permission to use PyTest for Entity Model
ref: #761 #730
2025-05-16 02:17:52 +09:30
Jon
2e096cb495 refactor(Access): Update Functional Permission to use PyTest for Contact Model
ref: #761 #730
2025-05-16 02:17:44 +09:30
Jon
3505f915c5 refactor(Access): Update Functional Serializer to use PyTest for Contact Model
ref: #761 #730
2025-05-16 02:17:31 +09:30
Jon
254cd02649 refactor(Access): Update Functional Serializer to use PyTest for Entity Model
ref: #761 #730
2025-05-16 02:17:21 +09:30
Jon
97e37f34a1 refactor(Access): Update Functional Serializer to use PyTest for Person Model
ref: #761 #730
2025-05-16 02:17:10 +09:30
Jon
ae6b66b270 refactor(human_resources): Update Functional Serializer to use PyTest for Employee Model
ref: #761 #730
2025-05-16 02:16:57 +09:30
Jon
7a1aecebd5 refactor(human_resources): Update Functional Permissions to use PyTest for Employee Model
ref: #761 #730
2025-05-16 02:16:49 +09:30
Jon
387ffc9ade refactor(human_resources): Update Functional Metadata to use PyTest for Employee Model
ref: #761 #730
2025-05-16 02:16:38 +09:30
Jon
d0c3537753 refactor(access): Update Functional Metadata to use PyTest for Person Model
ref: #761 #730
2025-05-16 02:16:19 +09:30
Jon
eb97bfbeeb refactor(access): Update Functional Metadata to use PyTest for Entity Model
ref: #761 #730
2025-05-16 02:16:09 +09:30
Jon
7fee33999f refactor(access): Update Functional Metadata to use PyTest for Contact Model
ref: #761 #730
2025-05-16 02:15:56 +09:30
Jon
99c0e92336 test(access): Functional Serializer Test Suite Company model
ref: #761 #760
2025-05-16 02:14:50 +09:30
Jon
46494c033c test(access): Functional Permissions Test Suite Company model
ref: #761 #760
2025-05-16 02:14:34 +09:30
Jon
85986ce13e test(access): Functional MetaData Test Suite Company model
ref: #761 #760
2025-05-16 02:14:23 +09:30
Jon
bdeae5b38b test(access): ViewSet Test Suite Company model
ref: #761 #760
2025-05-15 23:07:08 +09:30
Jon
edc7aedbe0 fix(api): Dont try to access attribute if not exist in common viewset
ref: #761
2025-05-15 23:03:49 +09:30
Jon
900e03791a test(access): API field render Test Suite Company model
ref: #761 #760
2025-05-15 22:18:45 +09:30
Jon
2a977bbf47 test(access): Model Test Suite Company model
ref: #761 #760
2025-05-15 22:18:29 +09:30
Jon
d29df73d05 refactor(access): Update Model Entity to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:17:36 +09:30
Jon
1150e1b047 refactor(access): Update Model Contact to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:16:56 +09:30
Jon
cf7eeb5bde refactor(access): Update Model Person to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:16:45 +09:30
Jon
c2a367bd28 refactor(human_resources): Update Model Employee to use PyTest for Model Test Suite
ref: #761 #730
2025-05-15 22:15:43 +09:30
Jon
3c19e9d4cf fix(api): Dont try to access attribute if not exist in common viewset
ref: #761
2025-05-15 20:19:43 +09:30
Jon
e295e53f86 refactor(human_resources): Update Model Employee to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:03:19 +09:30
Jon
be02061b94 refactor(access): Update Model Person to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:03:02 +09:30
Jon
08dbe1e35b refactor(access): Update Model Contact to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:02:50 +09:30
Jon
8e9dca56bc refactor(access): Update Model Entity to use PyTest API Fields Render
ref: #761 #730
2025-05-15 20:02:39 +09:30
Jon
2e9fe29e99 fix(api): Correct ViewSet Sub-Model lookup
ref: #761
2025-05-15 19:45:04 +09:30
Jon
b0a6f207cd docs(user): Add Conpany page
ref: #761 #760
2025-05-15 18:40:44 +09:30
Jon
a51a79a763 feat(access): model access.Company feature flag 2025-00008
ref: #761 #760
2025-05-15 18:34:51 +09:30
Jon
b2d1903009 feat(access): URL route for model access.Company
ref: #761 #760
2025-05-15 18:34:26 +09:30
Jon
53f99f620c feat(access): Migration for model access.Company
ref: #761 #760
2025-05-15 18:34:03 +09:30
Jon
312267567f feat(access): Serializer for model access.Company
ref: #761 #760
2025-05-15 18:33:52 +09:30
Jon
34b2571a2e feat(access): New model access.Company
ref: #761 #760
2025-05-15 18:33:21 +09:30
Jon
b79874d056 chore(access): Update tenant url
ref: #761 #505
2025-05-15 17:46:57 +09:30
Jon
27e42fac56 feat(access): Organization -> Tenant Permission Migration
ref: #761 #505
2025-05-15 17:46:00 +09:30
Jon
4124d5eb38 Merge pull request #756 from nofusscomputing/2025-05-14 2025-05-15 05:37:15 +09:30
Jon
bb8c6378bc chore: Add some tests to aid in planning
ref: #756 #758
2025-05-15 04:59:06 +09:30
Jon
c3109f1894 feat(docker): Serve a robots.txt file for NO indexing
ref: #756
2025-05-15 03:58:37 +09:30
Jon
59b4b5ff39 feat(access): Organization -> Tenant Permission Migration
ref: #756 #505
2025-05-15 02:45:03 +09:30
Jon
2d7335ff85 refactor(access): Rename model Organization -> Tenant
ref: #756 #505
2025-05-15 02:44:53 +09:30
Jon
2e49de8573 refactor(settings): Update all references to User to use get_user_model()
ref: #756 closes #755
2025-05-14 19:57:56 +09:30
Jon
a4772e3c25 refactor(project_management): Update all references to User to use get_user_model()
ref: #756 #755
2025-05-14 19:40:54 +09:30
Jon
de9937606e refactor(itam): Update all references to User to use get_user_model()
ref: #756 #755
2025-05-14 19:40:44 +09:30
Jon
1b749e9f1a refactor(devops): Update all references to User to use get_user_model()
ref: #756 #755
2025-05-14 18:48:56 +09:30
Jon
f0b3748596 Merge pull request #754 from nofusscomputing/test-ticket-comment-base 2025-05-14 05:11:27 +09:30
Jon
2ea42d10e6 refactor(core): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 04:37:38 +09:30
Jon
55cf10d289 refactor(config_management): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 04:10:29 +09:30
Jon
09ab52b971 refactor(assistance): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 04:04:51 +09:30
Jon
c00aa6fa73 refactor(app): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 03:59:15 +09:30
Jon
817c8d63e1 refactor(api): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 03:55:29 +09:30
Jon
cce885d961 refactor(accounting): Update all references to User to use get_user_model()
ref: #754 #755
2025-05-14 03:49:23 +09:30
Jon
6d92c484cd refactor(access): Update all references to User to use get_user_model()
ref: #754 #704 #755
2025-05-14 03:45:40 +09:30
Jon
f5be3b0f8e feat(base): Add var AUTH_USER_MODEL to settings
ref: #754 #704 #755
2025-05-14 03:41:00 +09:30
Jon
85afbbc01e chore(test): introduce further randomness to class fixtures so as not to create duplicate errors
ref: #754
2025-05-14 00:21:55 +09:30
Jon
e0c0c69d35 chore(test): Ensure comment cleanup during ticket tests
ref: #754 #726
2025-05-13 23:58:18 +09:30
Jon
df82650931 fix(core): Only take action on ticket comment if view exists
ref: #754 #726
2025-05-13 23:40:35 +09:30
Jon
3196006bad feat(core): Add Action comments on ticket change
ref: #754 #723
2025-05-13 23:31:08 +09:30
Jon
820755b9e0 chore(test): introduce further randomness to class fixtures so as not to create duplicate errors
ref: #754
2025-05-13 23:30:19 +09:30
Jon
1946c7aa88 fix(api): Ensure multi-nested searching for sub-models works
ref: #754
2025-05-13 23:29:02 +09:30
Jon
7c35a2d427 test(core): Unit viewset Test Cases for TicketCommentAction model
ref: #754 #736
2025-05-13 19:52:04 +09:30
Jon
c4ee706591 test(core): Unit model Test Cases for TicketCommentAction model
ref: #754 #736
2025-05-13 19:51:39 +09:30
Jon
4ee5f349f0 test(core): Unit API Render Test Cases for TicketCommentAction model
ref: #754 #736
2025-05-13 19:51:29 +09:30
Jon
21a1974ba1 test(core): Interim Functional model Test Case TicketCommentAction
ref: #754 #736
2025-05-13 19:50:51 +09:30
Jon
969b4d22fc feat(core): Remove add, change and delete permissions for model TicketCommentAction from permission selector
ref: #754 #736
2025-05-13 19:42:31 +09:30
Jon
6169324ffd feat(core): Serializer for model TicketCommentAction
ref: #754 #736
2025-05-13 19:22:00 +09:30
Jon
003cad1f58 feat(core): Migrations for model TicketCommentAction
ref: #754 #736
2025-05-13 19:19:23 +09:30
Jon
c8df4b21a0 feat(core): New model TicketCommentAction
ref: #754 #736
2025-05-13 19:19:10 +09:30
Jon
7f49fb2d0e feat(core): Setup serializer to meet requirements
ref: #754 #726
2025-05-13 18:58:18 +09:30
Jon
1cb028c273 feat(core): Setup model to meet requirements
ref: #754 #726
2025-05-12 20:11:01 +09:30
Jon
4ad6c10a64 chore(core): Remove commented code
ref: #754 #726
2025-05-12 20:10:28 +09:30
Jon
899df95994 feat(api): Add exception logging to ViewSetCommon
ref: #754 #752
2025-05-12 19:55:37 +09:30
Jon
0fa7ffa34a chore: correct dev log path to be <app root>/log
ref: #754
2025-05-12 19:36:08 +09:30
Jon
c46a597828 Merge pull request #753 from nofusscomputing/test-ticket-base-model 2025-05-12 06:46:17 +09:30
Jon
764f1b20d9 test(core): Ensure that a ticket milestone comes from the same assigned project
ref: #744 #723
2025-05-12 05:26:02 +09:30
Jon
01b4a681da test(core): SKIP Tests TicketBase Description Slash command Checks
Required until the slash commands comment creation uses the new ticketcommentbase model.

ref: #744 #723 #564 #746 #747
2025-05-12 04:07:19 +09:30
Jon
16299d480e test(core): TicketBase Description Slash command Checks
ref: #744 #723
2025-05-12 04:05:53 +09:30
Jon
b0cb9f6fd0 chore(test): Add global support not to generate tests that are marked skip or class skip
ref: #753 #739
2025-05-12 04:04:32 +09:30
Jon
5b133c951d chore(test): Add a global fixture to simulate a viewset
ref: #753 #723 #730
2025-05-12 02:53:19 +09:30
Jon
e29a7ec0e2 test(core): TicketBase Remaining Serializer Chacks
ref: #753 #723
2025-05-12 02:52:34 +09:30
Jon
e7213d8a70 Merge pull request #744 from nofusscomputing/test-ticket-comment-base 2025-05-11 02:31:54 +09:30
Jon
4d7510ad3a feat(python): Upgrade DRF Spectacular 0.27.2 -> 0.28.0
ref: #744
2025-05-11 01:48:46 +09:30
Jon
d8ef918a67 feat(python): Upgrade DRF 3.15.2 -> 3.16.0
ref: #744
2025-05-11 01:41:30 +09:30
Jon
cb49d0fbf7 feat(core): When processing slash command duration, cater for new ticket models
ref: #744 #728 #726 #746 #747
2025-05-10 23:42:40 +09:30
Jon
70c835eb93 test(core): Partial functional Model Test Suite covering some slash commande for TicketCommentSolution
ref: #744 #728
2025-05-10 23:40:41 +09:30
Jon
40b51f1a77 feat(api): Add Logging function to Common ViewSet
ref: #744 #436 #752
2025-05-10 23:39:22 +09:30
Jon
1bff76c637 feat(access): Add Logging function to Tenancy model
ref: #744 #436 #752
2025-05-10 23:38:58 +09:30
Jon
24b6bcfa47 feat(base): Enable user to customize log file location
ref: #744 #436 #752
2025-05-10 23:37:54 +09:30
Jon
b22baefa5a test(core): ensure ticket is un-solved for ticketcomment unit api render fields check
ref: #744 #726
2025-05-10 17:27:27 +09:30
Jon
a6e0f4e728 test(core): ensure slash command is called on ticket comment
ref: #744 #726
2025-05-10 17:23:37 +09:30
Jon
e366220e8b fix(core): ensure slash command is called on ticket description
ref: #744 #723
2025-05-10 16:42:23 +09:30
Jon
f71e304731 test(core): Unit ViewSet Test Suite for TicketCommentSolution
ref: #744 #728
2025-05-10 15:24:50 +09:30
Jon
55f58bb689 test(core): Unit ViewSet Test Suite for TicketCommentBase
ref: #744 #726
2025-05-10 15:24:27 +09:30
Jon
03b752759f test(core): Skip Related slash command checks until migrating tickets to new model
ref: #744 #746
2025-05-10 14:34:39 +09:30
Jon
85c6ed8483 test(core): Add ability to unit api field rendering test case for second api request if required
ref: #744 #726
2025-05-10 13:26:22 +09:30
Jon
a081c6a371 test(core): Partial Functional Model test cases (Slash Commands) for TicketCommentBase
ref: #744 #726
2025-05-09 21:05:54 +09:30
Jon
6c3122a3d8 test(core): Functional Model test cases (Slash Commands) for TicketBaseModel
ref: #744 #723
2025-05-09 21:04:31 +09:30
Jon
0c80b87606 test(core): Partial Slash Command re-write
ref: #744 #730
2025-05-09 21:03:44 +09:30
Jon
b6da539fcd fix(core): Spent slash command is valid for time spent
ref: #744
2025-05-09 21:02:55 +09:30
Jon
d4d99772b9 test(core): correct field so its valid for unit TicketCommentBase model
ref: #744 #726
2025-05-09 20:42:16 +09:30
Jon
4f8be43527 test(core): Unit API Fields Render for TicketCommentSolution model
ref: #744 #728
2025-05-09 20:41:26 +09:30
Jon
eb2282efac test(core): Unit API Fields Render for TicketCommentBase model
ref: #744 #726
2025-05-09 20:40:45 +09:30
Jon
85b5bf7b58 feat(core): Do validate the comment_type field for TicketCommentBase
ref: #744 #726
2025-05-09 20:08:06 +09:30
Jon
626a5ccb11 refactor(access): when fetching parent object, use the parent_model get function
ref: #744
2025-05-09 20:06:45 +09:30
Jon
806ffb2754 chore(python): upgrade django 5.1.8 -> 5.1.9
ref: #744
2025-05-09 16:25:33 +09:30
Jon
5900c13e08 docs(development): Add initial TicketCommentBase
ref: #744 #726
2025-05-09 16:21:42 +09:30
Jon
d399698eb1 test(core): Unit Model assert save and call are called for TicketBase
ref: #744 #723
2025-05-09 16:20:44 +09:30
Jon
457d329b0b fix(core): Correct logic for TicketCommentSolution
ref: #744 #728
2025-05-09 16:19:57 +09:30
Jon
45c428d30a fix(core): Correct logic for TicketCommentBase
ref: #744 #726
2025-05-09 16:19:33 +09:30
Jon
7ddb72239e chore(python): Add testing dep pytest-mock
ref: #744
2025-05-09 16:13:38 +09:30
Jon
580820ef44 test(core): Unit Model Checks for TicketCommentSolution
ref: #744 #728
2025-05-09 16:11:59 +09:30
Jon
d037150eb3 test(core): Unit Model Checks for TicketCommentBase
ref: #744 #726
2025-05-09 16:10:45 +09:30
Jon
35a102e4a9 Merge pull request #742 from nofusscomputing/test-asset-base 2025-05-08 15:44:35 +09:30
Jon
f7c75df9be docs(itam): Add IT Asset
ref: #742 closes #692
2025-05-08 15:30:11 +09:30
Jon
d0e18fe75a docs(development): Add Asset
ref: #742 closes #737
2025-05-08 15:29:59 +09:30
Jon
e30c08fd5b test(itam): test meta attribute itam_sub_model_type for ITAMBaseModel
ref: #742 #692
2025-05-08 15:10:38 +09:30
Jon
1058659174 test(itam): Dont use constants where variables should be used
ref: #742
2025-05-08 14:54:03 +09:30
Jon
b9ac588f87 test(itam): Remaining Unit Model test cases for AssetBase
ref: #742 #692
2025-05-08 14:54:03 +09:30
Jon
40b4bb0a3e test(accounting): Remaining Unit Model test cases for AssetBase
ref: #742 #737
2025-05-08 14:54:03 +09:30
Jon
9bd9652b3d fix(accounting): Ensure correct sub-model check is conducted within model type
ref: #742 #737
2025-05-08 14:47:56 +09:30
Jon
46c4fe9516 fix(itam): ensure RO field asset_type is set
ref: #742 #692
2025-05-08 14:25:47 +09:30
Jon
a71b5e6aba test(itam): Functional ViewSet Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:42:24 +09:30
Jon
e0cbf1447f test(itam): Functional Serializer Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:42:06 +09:30
Jon
b69e54f1e9 test(itam): Functional Permissions Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:41:56 +09:30
Jon
ecb1c21179 test(itam): Functional Metadata Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:41:49 +09:30
Jon
acff19ad58 test(itam): Functional History Test Cases for ITAMAssetBase
ref: #742 #692
2025-05-08 13:41:41 +09:30
Jon
f79f796a14 test(accounting): Functional ViewSet Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:59 +09:30
Jon
a08a43e2bd test(accounting): Functional Serializer Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:51 +09:30
Jon
8238e97a45 test(accounting): Functional Permissions Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:44 +09:30
Jon
88202b57ee test(accounting): Functional Metadata Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:36 +09:30
Jon
324fa39b56 test(accounting): History Test Cases for AssetBase
ref: #742 #737
2025-05-08 13:40:23 +09:30
Jon
233393e853 test: add missing merge of add_data for api permissions tests
ref: #742 #730
2025-05-08 13:38:56 +09:30
Jon
84afc3274a test: remove ticket only vars from api permissions tests
ref: #742 #730
2025-05-08 12:28:19 +09:30
Jon
a5bfc4977d Merge pull request #741 from nofusscomputing/base-asset-model 2025-05-06 04:20:14 +09:30
Jon
19205bbe8b feat(itam): Add Feature Flag 2025-00007 ITAMAssetBase
ref: #741 #692
2025-05-06 03:58:29 +09:30
Jon
f144e0b8ef feat(itam): Add endpoint for ITAMAssetBase
ref: #741 #692
2025-05-06 03:58:01 +09:30
Jon
8473d00148 feat: Model tag migration for Asset and IT Asset
ref: #741 #737 #692
2025-05-06 02:09:44 +09:30
Jon
c8391caf07 feat(itam): Model tag for ITAsset
ref: #741 #692
2025-05-06 02:08:17 +09:30
Jon
d19bb3a204 feat(accounting): Model tag for Asset
ref: #741 #737
2025-05-06 02:07:38 +09:30
Jon
ed71e935fc test(api): dont use constants for variable data
ref: #741
2025-05-06 01:43:38 +09:30
Jon
5ba243a1ea test: correct viewset tests
ref: #741
2025-05-06 00:48:29 +09:30
Jon
88a30650a5 test(itam): Unit Viewset checks for AssetBase Model
ref: #741 #692
2025-05-05 22:10:50 +09:30
Jon
420b223ca4 test(core): Add missing fields is_global checks for ticket base
ref: #741 #723
2025-05-05 22:08:42 +09:30
Jon
7168d519d1 test(api): Add submodel url resolution for metadata
ref: #741
2025-05-05 22:07:56 +09:30
Jon
4a09463f0a test(itam): Unit API Fields checks for ITAM AssetBase Model
ref: #741 #692
2025-05-05 20:45:30 +09:30
Jon
0a52029840 test(accounting): Unit API Fields checks for AssetBase Model
ref: #741 #737
2025-05-05 20:44:56 +09:30
Jon
6841b30a77 test: Support variables that were defined as properties.
ref: #741
2025-05-05 20:34:23 +09:30
Jon
dbaff89b8d test(api): Ensure that model notes is added to model create for api field tests
ref: #741
2025-05-05 17:49:17 +09:30
Jon
b7cd9ea75c fix(itim): Ensure that itam base model is always imported
ref: #741 #692
2025-05-05 17:33:30 +09:30
Jon
b54f3b7ab4 refactor(api): Limit url pk regex to ensure the value is a number
ref: #741
2025-05-05 17:22:08 +09:30
Jon
83d7c38c38 docs(accounting): added interim
ref: #741 #737
2025-05-05 16:58:44 +09:30
Jon
644dbc8159 feat(accounting): Add app label to kb articles for notes
ref: #741 #737
2025-05-05 16:38:41 +09:30
Jon
e566a5edf1 chore: add link to issue template for clarity of sub-model history variables
ref: #741
2025-05-05 16:35:03 +09:30
Jon
ef5c6b73af feat(accounting): Migrations for notes model for AssetBase
ref: #741 #737
2025-05-05 16:24:34 +09:30
Jon
3d7b133005 feat(accounting): Migrations for history model for AssetBase
ref: #741 #737
2025-05-05 16:24:24 +09:30
Jon
37a18d3c6e feat(accounting): Notes Viewset for AssetBase
ref: #741 #737
2025-05-05 16:24:00 +09:30
Jon
d6290471ba feat(accounting): Notes Serializer for AssetBase
ref: #741 #737
2025-05-05 16:23:33 +09:30
Jon
c13b360ca5 feat(accounting): Notes model for AssetBase
ref: #741 #737
2025-05-05 16:23:14 +09:30
Jon
e5cb0261ba feat(accounting): History model for AssetBase
ref: #741 #737
2025-05-05 16:22:48 +09:30
Jon
6c91cb008c chore(itam): just add this fucking thing cause vscode is a cunt and wont pick up the test correctly as its using an old value that does not exist even though the fucking code works directly from the console via pytest.
ref: #741 #692
2025-05-05 04:56:30 +09:30
Jon
9561301500 test(accounting): Unit Viewset checks for AssetBase Model
ref: #741 #737
2025-05-05 04:21:45 +09:30
Jon
349d67fa7b test(itam): Unit Model checks for ITAMAssetBase Model
ref: #741 #692
2025-05-05 02:46:41 +09:30
Jon
ce64664447 test(base): update Model base test suite for model_notes field
ref: #741 #737
2025-05-05 02:45:19 +09:30
Jon
370b8cd40f test(accounting): Unit Model checks for AssetBase Model
ref: #741 #737
2025-05-05 02:43:09 +09:30
Jon
efde919689 feat(itam): Serializer for ITAssetBase
ref: #741 #692
2025-05-05 01:00:14 +09:30
Jon
9a88b75654 feat(itam): Migrations for ITAssetBase
ref: #741 #692
2025-05-05 00:59:51 +09:30
Jon
0ceab03334 feat(itam): Add Model ITAssetBase
ref: #741 #692
2025-05-05 00:59:23 +09:30
Jon
c8cec06d85 feat(accounting): Viewset for Assets
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
018cd7d245 feat(accounting): Serializer for model AssetBase
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
471b5c08f6 feat(accounting): Migrations for model AssetBase
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
b11a978962 feat(accounting): Add Model AssetBase
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
95aba2b44b chore(accounting): Add permissions to permissions selector
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
5d6d0e95ec chore(accounting): Add viewsets directory
ref: #741 #737
2025-05-05 00:58:08 +09:30
Jon
f0f75ecaa8 chore(accounting): Add models directory
ref: #740 #737
2025-05-04 21:16:29 +09:30
1596 changed files with 45105 additions and 61391 deletions

View File

@ -17,5 +17,5 @@ commitizen:
prerelease_offset: 1
tag_format: $version
update_changelog_on_bump: false
version: 1.16.0
version: 1.17.1
version_scheme: semver

View File

@ -33,39 +33,22 @@ Describe in detail the following:
- [ ] 🛠️ Migrations added
- [ ] ♻️ Serializer Created
- [ ] 🔄 [ViewSet Created](https://nofusscomputing.com/projects/centurion_erp/development/views/)
- [ ] 🔗 URL Route Added
- [ ] 🏷️ Model tag added to `app/core/lib/slash_commands/linked_model.CommandLinkedModel.get_model()` function
- [ ] 🏷️ [Model tag]().
- [ ] 📘 Tag updated in the [docs](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
- [ ] tag added to `app/core/lib/slash_commands/linked_model.CommandLinkedModel.get_model()`
- [ ] ⚒️ Migration _Ticket Linked Item item_type choices update_
>[!note]
> Ensure that when creating the tag the following is adhered to:
> - Two words are not to contain a space char, `\s`. It is to be replaced with an underscore `_`
> - As much as practical, keep the tag as close to the model name as possible
- [ ] 📝 New [History model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_history/) created
- Sub-Models **_ONLY_**
- [ ] Model class variable `history_app_label` set to correct application label
- [ ] Model class variable `history_model_name` set to correct model label
- [ ] 📓 New [Notes model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/) created
- [ ] 🆕 Model Created
- [ ] 🛠️ Migrations added
- [ ] Add `app_label` to KB Models `app/assistance/models/model_knowledge_base_article.all_models().model_apps`
- [ ] _(Notes not used/required) -_ Add `model_name` to KB Models `app/assistance/models/model_knowledge_base_article.all_models().excluded_models`
- [ ] 🧪 [Unit tested](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/#testing)
- [ ] 🧪 [Functional tested](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/#testing)
- [ ] tag added to class
- [ ] Admin Documentation added/updated _if applicable_
- [ ] Developer Documentation added/updated _if applicable_
- [ ] User Documentation added/updated
---
@ -78,15 +61,15 @@ Describe in detail the following:
- Unit Tests
- [ ] [Model](https://nofusscomputing.com/projects/centurion_erp/development/models/#tests)
- [ ] ViewSet
- [ ] Serializer
- [ ] ViewSet
- Function Test
- [ ] ViewSet
- [ ] API Metadata
- [ ] API Permissions
- [ ] API Render (fields)
- [ ] History Entries
- [ ] History API Render (fields)
- [ ] Model
- [ ] Serializer
- [ ] ViewSet
## ✅ Requirements
@ -95,6 +78,24 @@ A Requirement is a must have. In addition will also be tested.
- [ ] Must have a [model_tag](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
<!--
When detailing requirements the following must be taken into account:
- what the user should be able to do
- what the user should not be able to do
- what should occur when a user performs an action
-->
- Functional Requirements
- Non-Functional Requirements
---
<!-- Add additional requirement here and as a check box list -->

4
.gitignore vendored
View File

@ -1,9 +1,10 @@
venv/**
*/static/**
__pycache__
**.sqlite3
**.sqlite*
**.sqlite
**.coverage
.coverage*
artifacts/
**.tmp.*
volumes/
@ -19,3 +20,4 @@ package.json
feature_flags.json
coverage_*.json
*-coverage.xml
log/

View File

@ -8,5 +8,6 @@
"qwtel.sqlite-viewer",
"jebbs.markdown-extended",
"william-voyek.vscode-nginx",
"detachhead.basedpyright",
]
}

14
.vscode/launch.json vendored
View File

@ -29,7 +29,7 @@
"3",
"--bind",
"0.0.0.0:8002",
"app.wsgi:application",
"centurion.wsgi:application",
],
"django": true,
"autoStartBrowser": false,
@ -50,6 +50,18 @@
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Centurion Model (Management Command)",
"type": "debugpy",
"request": "launch",
"args": [
"models",
// "0.0.0.0:8002"
],
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Migrate",
"type": "debugpy",

View File

@ -6,6 +6,7 @@
],
"python.testing.pytestArgs": [
"--override-ini", "addopts=",
"--no-migrations",
"app",
],
"python.testing.unittestEnabled": false,
@ -23,4 +24,6 @@
"yellow": 60,
"green": 90
},
"telemetry.feedback.enabled": false,
"python.languageServer": "None",
}

View File

@ -1,3 +1,176 @@
## 1.17.1 (2025-06-02)
### Fixes
- **base**: Add python metrics to prometheus exporter
## 1.17.0 (2025-05-16)
### feat
- **access**: model access.Company feature flag `2025-00008`
- **access**: URL route for model access.Company
- **access**: Migration for model access.Company
- **access**: Serializer for model access.Company
- **access**: New model access.Company
- **access**: Organization -> Tenant Permission Migration
- **docker**: Serve a robots.txt file for NO indexing
- **access**: Organization -> Tenant Permission Migration
- **base**: Add var `AUTH_USER_MODEL` to settings
- **core**: Add Action comments on ticket change
- **core**: Remove add, change and delete permissions for model TicketCommentAction from permission selector
- **core**: Serializer for model TicketCommentAction
- **core**: Migrations for model TicketCommentAction
- **core**: New model TicketCommentAction
- **core**: Setup serializer to meet requirements
- **core**: Setup model to meet requirements
- **api**: Add exception logging to ViewSetCommon
- **python**: Upgrade DRF Spectacular 0.27.2 -> 0.28.0
- **python**: Upgrade DRF 3.15.2 -> 3.16.0
- **core**: When processing slash command duration, cater for new ticket models
- **api**: Add Logging function to Common ViewSet
- **access**: Add Logging function to Tenancy model
- **base**: Enable user to customize log file location
- **core**: Do validate the comment_type field for TicketCommentBase
- **itam**: Add Feature Flag `2025-00007` ITAMAssetBase
- **itam**: Add endpoint for ITAMAssetBase
- Model tag migration for Asset and IT Asset
- **itam**: Model tag for ITAsset
- **accounting**: Model tag for Asset
- **accounting**: Add app label to kb articles for notes
- **accounting**: Migrations for notes model for AssetBase
- **accounting**: Migrations for history model for AssetBase
- **accounting**: Notes Viewset for AssetBase
- **accounting**: Notes Serializer for AssetBase
- **accounting**: Notes model for AssetBase
- **accounting**: History model for AssetBase
- **itam**: Serializer for ITAssetBase
- **itam**: Migrations for ITAssetBase
- **itam**: Add Model ITAssetBase
- **accounting**: Viewset for Assets
- **accounting**: Serializer for model AssetBase
- **accounting**: Migrations for model AssetBase
- **accounting**: Add Model AssetBase
### Fixes
- **api**: Dont try to access attribute if not exist in common viewset
- **api**: Dont try to access attribute if not exist in common viewset
- **api**: Correct ViewSet Sub-Model lookup
- **core**: Only take action on ticket comment if view exists
- **api**: Ensure multi-nested searching for sub-models works
- **core**: ensure slash command is called on ticket description
- **core**: Spent slash command is valid for time spent
- **core**: Correct logic for TicketCommentSolution
- **core**: Correct logic for TicketCommentBase
- **accounting**: Ensure correct sub-model check is conducted within model type
- **itam**: ensure RO field asset_type is set
- **itim**: Ensure that itam base model is always imported
### Refactoring
- **human_resources**: Update Functional ViewSet to use PyTest for Employee Model
- **Access**: Update Functional ViewSet to use PyTest for Person Model
- **Access**: Update Functional ViewSet to use PyTest for Entity Model
- **Access**: Update Functional ViewSet to use PyTest for Contact Model
- **Access**: Update Functional Permission to use PyTest for Person Model
- **Access**: Update Functional Permission to use PyTest for Entity Model
- **Access**: Update Functional Permission to use PyTest for Contact Model
- **Access**: Update Functional Serializer to use PyTest for Contact Model
- **Access**: Update Functional Serializer to use PyTest for Entity Model
- **Access**: Update Functional Serializer to use PyTest for Person Model
- **human_resources**: Update Functional Serializer to use PyTest for Employee Model
- **human_resources**: Update Functional Permissions to use PyTest for Employee Model
- **human_resources**: Update Functional Metadata to use PyTest for Employee Model
- **access**: Update Functional Metadata to use PyTest for Person Model
- **access**: Update Functional Metadata to use PyTest for Entity Model
- **access**: Update Functional Metadata to use PyTest for Contact Model
- **access**: Update Model Entity to use PyTest for Model Test Suite
- **access**: Update Model Contact to use PyTest for Model Test Suite
- **access**: Update Model Person to use PyTest for Model Test Suite
- **human_resources**: Update Model Employee to use PyTest for Model Test Suite
- **human_resources**: Update Model Employee to use PyTest API Fields Render
- **access**: Update Model Person to use PyTest API Fields Render
- **access**: Update Model Contact to use PyTest API Fields Render
- **access**: Update Model Entity to use PyTest API Fields Render
- **access**: Rename model Organization -> Tenant
- **settings**: Update all references to `User` to use `get_user_model()`
- **project_management**: Update all references to `User` to use `get_user_model()`
- **itam**: Update all references to `User` to use `get_user_model()`
- **devops**: Update all references to `User` to use `get_user_model()`
- **core**: Update all references to `User` to use `get_user_model()`
- **config_management**: Update all references to `User` to use `get_user_model()`
- **assistance**: Update all references to `User` to use `get_user_model()`
- **app**: Update all references to `User` to use `get_user_model()`
- **api**: Update all references to `User` to use `get_user_model()`
- **accounting**: Update all references to `User` to use `get_user_model()`
- **access**: Update all references to `User` to use `get_user_model()`
- **access**: when fetching parent object, use the parent_model get function
- **api**: Limit url pk regex to ensure the value is a number
### Tests
- **access**: Functional ViewSet Test Suite Company model
- **access**: Functional Serializer Test Suite Company model
- **access**: Functional Permissions Test Suite Company model
- **access**: Functional MetaData Test Suite Company model
- **access**: ViewSet Test Suite Company model
- **access**: API field render Test Suite Company model
- **access**: Model Test Suite Company model
- **core**: Unit viewset Test Cases for TicketCommentAction model
- **core**: Unit model Test Cases for TicketCommentAction model
- **core**: Unit API Render Test Cases for TicketCommentAction model
- **core**: Interim Functional model Test Case TicketCommentAction
- **core**: Ensure that a ticket milestone comes from the same assigned project
- **core**: SKIP Tests TicketBase Description Slash command Checks
- **core**: TicketBase Description Slash command Checks
- **core**: TicketBase Remaining Serializer Chacks
- **core**: Partial functional Model Test Suite covering some slash commande for TicketCommentSolution
- **core**: ensure ticket is un-solved for ticketcomment unit api render fields check
- **core**: ensure slash command is called on ticket comment
- **core**: Unit ViewSet Test Suite for TicketCommentSolution
- **core**: Unit ViewSet Test Suite for TicketCommentBase
- **core**: Skip Related slash command checks until migrating tickets to new model
- **core**: Add ability to unit api field rendering test case for second api request if required
- **core**: Partial Functional Model test cases (Slash Commands) for TicketCommentBase
- **core**: Functional Model test cases (Slash Commands) for TicketBaseModel
- **core**: Partial Slash Command re-write
- **core**: correct field so its valid for unit TicketCommentBase model
- **core**: Unit API Fields Render for TicketCommentSolution model
- **core**: Unit API Fields Render for TicketCommentBase model
- **core**: Unit Model assert save and call are called for TicketBase
- **core**: Unit Model Checks for TicketCommentSolution
- **core**: Unit Model Checks for TicketCommentBase
- **itam**: test meta attribute itam_sub_model_type for ITAMBaseModel
- **itam**: Dont use constants where variables should be used
- **itam**: Remaining Unit Model test cases for AssetBase
- **accounting**: Remaining Unit Model test cases for AssetBase
- **itam**: Functional ViewSet Test Cases for ITAMAssetBase
- **itam**: Functional Serializer Test Cases for ITAMAssetBase
- **itam**: Functional Permissions Test Cases for ITAMAssetBase
- **itam**: Functional Metadata Test Cases for ITAMAssetBase
- **itam**: Functional History Test Cases for ITAMAssetBase
- **accounting**: Functional ViewSet Test Cases for AssetBase
- **accounting**: Functional Serializer Test Cases for AssetBase
- **accounting**: Functional Permissions Test Cases for AssetBase
- **accounting**: Functional Metadata Test Cases for AssetBase
- **accounting**: History Test Cases for AssetBase
- add missing merge of add_data for api permissions tests
- remove ticket only vars from api permissions tests
- **api**: dont use constants for variable data
- correct viewset tests
- **itam**: Unit Viewset checks for AssetBase Model
- **core**: Add missing fields is_global checks for ticket base
- **api**: Add submodel url resolution for metadata
- **itam**: Unit API Fields checks for ITAM AssetBase Model
- **accounting**: Unit API Fields checks for AssetBase Model
- Support variables that were defined as properties.
- **api**: Ensure that model notes is added to model create for api field tests
- **accounting**: Unit Viewset checks for AssetBase Model
- **itam**: Unit Model checks for ITAMAssetBase Model
- **base**: update Model base test suite for model_notes field
- **accounting**: Unit Model checks for AssetBase Model
## 1.16.0 (2025-05-04)
### feat

View File

@ -1,3 +1,45 @@
## Version 1.18.0
- Added new model for History
!!! info
Migration of the old history tables to the new history tables occurs as part of post migration. As such the time it will take to migrate the history is dependent upon how many history entries per model. This should be planned for when upgrading to this version. if for some reason the migration is interrupted, you can safely restart it again by running the migrate command.
- Added new model for notes
!!! info
Migration of the old notes tables to the new note tables occurs as part of post migration. As such the time it will take to migrate the history is dependent upon how many history entries per model. This should be planned for when upgrading to this version. if for some reason the migration is interrupted, you can safely restart it again by running the migrate command.
- Removed Django UI
[UI](https://github.com/nofusscomputing/centurion_erp) must be deployed seperatly.
- Removed API v1
## Version 1.17.0
- Added setting for log files.
Enables user to specify a default path for centurion's logging. Add the following to your settings file `/etc/itsm/settings.py`
``` py
LOG_FILES = {
"centurion": "/var/log/centurion.log", # Normal Centurion Operations
"weblog": "/var/log/weblog.log", # All web requests made to Centurion
"rest_api": "/var/log/rest_api.log", # Rest API
"catch_all":"/var/log/catch-all.log" # A catch all log. Note: does not log anything that has already been logged.
}
```
With this new setting, the previous setting `LOGGING` will no longer function.
- Renamed `Organization` model to `Tenant` so as to reflect what is actually is.
- `robots.txt` file now being served from the API container at path `/robots.txt` with `User-agent: *` and `Disallow: /`
## Version 1.16.0
- Employees model added behind feature flag `2025-00002` and will remain behind this flag until production ready.

View File

@ -1,12 +1,15 @@
import django
from django.contrib import admin
from django.contrib.auth.models import Group, User
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
User = django.contrib.auth.get_user_model()
admin.site.unregister(Group)
class TeamInline(admin.TabularInline):

View File

@ -1,6 +1,7 @@
from django.db import models
from django.utils.timezone import now
from django.template.defaultfilters import slugify
class AutoCreatedField(models.DateTimeField):
"""
@ -50,7 +51,7 @@ class AutoLastModifiedField(AutoCreatedField):
def pre_save(self, model_instance, add):
value = now()
value = now().replace(microsecond=0)
setattr(model_instance, self.attname, value)

View File

@ -1,38 +0,0 @@
from django import forms
from django.db.models import Q
from app import settings
from access.models.organization import Organization
from core.forms.common import CommonModelForm
class OrganizationForm(CommonModelForm):
class Meta:
model = Organization
fields = [
'name',
'manager',
'model_notes',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['created'] = forms.DateTimeField(
label="Created",
input_formats=settings.DATETIME_FORMAT,
initial=kwargs['instance'].created,
disabled=True,
required=False,
)
self.fields['modified'] = forms.DateTimeField(
label="Modified",
input_formats=settings.DATETIME_FORMAT,
initial=kwargs['instance'].modified,
disabled=True,
required=False,
)

View File

@ -1,69 +0,0 @@
from django import forms
from django.db.models import Q
from django.forms import inlineformset_factory
from .team_users import TeamUsersForm, TeamUsers
from access.models.team import Team
from access.functions import permissions
from app import settings
from core.forms.common import CommonModelForm
TeamUserFormSet = inlineformset_factory(
model=TeamUsers,
parent_model= Team,
extra = 1,
fields=[
'user',
'manager'
]
)
class TeamFormAdd(CommonModelForm):
class Meta:
model = Team
fields = [
'team_name',
'model_notes',
]
class TeamForm(CommonModelForm):
class Meta:
model = Team
fields = [
'team_name',
'permissions',
'model_notes',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['created'] = forms.DateTimeField(
label="Created",
input_formats=settings.DATETIME_FORMAT,
initial=kwargs['instance'].created,
disabled=True,
required=False,
)
self.fields['modified'] = forms.DateTimeField(
label="Modified",
input_formats=settings.DATETIME_FORMAT,
initial=kwargs['instance'].modified,
disabled=True,
required=False,
)
self.fields['permissions'].widget.attrs = {'style': "height: 200px;"}
self.fields['permissions'].queryset = permissions.permission_queryset()

View File

@ -1,16 +0,0 @@
from django.db.models import Q
from app import settings
from access.models.team_user import TeamUsers
from core.forms.common import CommonModelForm
class TeamUsersForm(CommonModelForm):
class Meta:
model = TeamUsers
fields = [
'user',
'manager',
]

View File

@ -9,6 +9,7 @@ def permission_queryset():
apps = [
'access',
'accounting',
'assistance',
'config_management',
'core',
@ -36,14 +37,17 @@ def permission_queryset():
'add_history',
'add_organization',
'add_taskresult',
'add_ticketcommentaction',
'change_checkin',
'change_history',
'change_organization',
'change_taskresult',
'change_ticketcommentaction',
'delete_checkin',
'delete_history',
'delete_organization',
'delete_taskresult',
'delete_ticketcommentaction',
'view_checkin',
'view_history',
]

View File

@ -1,18 +1,17 @@
from django.contrib.auth.middleware import (
AuthenticationMiddleware,
SimpleLazyObject,
partial,
)
from django.contrib.auth.models import User, Group
import django
from django.contrib.auth.models import Group
from django.utils.deprecation import MiddlewareMixin
from access.models.organization import Organization
from access.models.tenant import Tenant
from access.models.team import Team
from settings.models.app_settings import AppSettings
User = django.contrib.auth.get_user_model()
class RequestTenancy(MiddlewareMixin):
"""Access Middleware
@ -24,9 +23,9 @@ class RequestTenancy(MiddlewareMixin):
def process_request(self, request):
request.app_settings = AppSettings.objects.select_related('global_organization').get(
request.app_settings = AppSettings.objects.select_related('global_organization').filter(
owner_organization = None
)
)[0]
request.tenancy = Tenancy(user = request.user, app_settings = request.app_settings)
@ -41,8 +40,8 @@ class Tenancy:
_app_settings: AppSettings = None
_user_organizations: list([Organization]) = None
"""Cached User Organizations"""
_user_organizations: list([Tenant]) = None
"""Cached User Tenants"""
_user_teams: list([Team]) = None
"""Cached User Teams"""
@ -91,7 +90,7 @@ class Tenancy:
def is_member(self, organization: Organization) -> bool:
def is_member(self, organization: Tenant) -> bool:
"""Returns true if the current user is a member of the organization
iterates over the user_organizations list and returns true if the user is a member
@ -114,11 +113,11 @@ class Tenancy:
def has_organization_permission(self, organization: Organization, permissions_required: str) -> bool:
def has_organization_permission(self, organization: Tenant, permissions_required: str) -> bool:
""" Check if user has permission within organization.
Args:
organization (int): Organization to check.
organization (int): Tenant to check.
permissions_required (list): if doing object level permissions, pass in required permission.
Returns:
@ -127,9 +126,9 @@ class Tenancy:
has_permission: bool = False
if type(organization) is not Organization:
if type(organization) is not Tenant:
raise TypeError('Organization must be of type Organization')
raise TypeError('Tenant must be of type Tenant')
if type(permissions_required) is not str:

View File

@ -0,0 +1,25 @@
# Generated by Django 5.1.9 on 2025-05-14 11:06
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('access', '0005_entity_person_entityhistory_entitynotes_role_and_more'),
('assistance', '0005_knowledgebasecategoryhistory_knowledgebasehistory'),
('config_management', '0007_configgroupshistory_configgrouphostshistory_and_more'),
('core', '0022_ticketcommentbase_ticketbase_ticketcommentsolution_and_more'),
('devops', '0011_alter_gitgroup_unique_together_and_more'),
('itam', '0010_alter_software_organization'),
('itim', '0009_slmticket_requestticket'),
('project_management', '0005_projecthistory_projectmilestonehistory_and_more'),
('settings', '0011_appsettingshistory_externallinkhistory'),
]
operations = [
migrations.RenameModel(
old_name = 'Organization',
new_name = 'Tenant'
),
]

View File

@ -0,0 +1,47 @@
# Generated by Django 5.1.9 on 2025-05-14 13:48
import access.models.team
import access.models.tenancy
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0007_rename_organization_tenant'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterModelOptions(
name='tenant',
options={'ordering': ['name'], 'verbose_name': 'Tenant', 'verbose_name_plural': 'Tenants'},
),
migrations.AlterField(
model_name='entity',
name='organization',
field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'),
),
migrations.AlterField(
model_name='role',
name='organization',
field=models.ForeignKey(help_text='Tenancy this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.tenant', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Tenant'),
),
migrations.AlterField(
model_name='team',
name='organization',
field=models.ForeignKey(help_text='Tenant this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.tenant', validators=[access.models.team.Team.validatate_organization_exists], verbose_name='Tenant'),
),
migrations.AlterField(
model_name='tenant',
name='manager',
field=models.ForeignKey(help_text='Manager for this Tenancy', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Manager'),
),
migrations.AlterField(
model_name='tenant',
name='name',
field=models.CharField(help_text='Name of this Tenancy', max_length=50, unique=True, verbose_name='Name'),
),
]

View File

@ -0,0 +1,135 @@
from django.contrib.auth.models import ContentType, Permission
from django.db import migrations
from access.models.team import Team
ContentType.DoesNotExist
def add_tenancy_permissions(apps, schema_editor):
print('')
print(f"Begin permission migration for rename of Organization to Tenant.")
try:
add_permission = Permission.objects.get(
codename = 'add_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
change_permission = Permission.objects.get(
codename = 'change_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
delete_permission = Permission.objects.get(
codename = 'delete_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
view_permission = Permission.objects.get(
codename = 'view_tenant',
content_type = ContentType.objects.get(
app_label = 'access',
model = 'tenant',
)
)
print(f' Searching for Teams.')
teams = Team.objects.select_related('group_ptr__permissions')
print(f'Found {str(len(teams))} Teams.')
for team in teams:
print(f' Processing Team {str(team.team_name)}.')
permissions = team.group_ptr.permissions.all()
print(f' Searching for Organization Permissions.')
print(f' Found {str(len(permissions))} Permissions.')
for permission in permissions:
if '_organization' not in permission.codename:
continue
action = str(permission.codename).split('_')[0]
print(f' Found Organization Permission {str(action)}')
if action == 'add':
team.group_ptr.permissions.add( add_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
elif action == 'change':
team.group_ptr.permissions.add( change_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
elif action == 'delete':
team.group_ptr.permissions.add( delete_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
elif action == 'view':
team.group_ptr.permissions.add( view_permission )
print(f' Add Tenant Permission {str(action)}')
team.group_ptr.permissions.remove( permission )
print(f' Remove Organization Permission {str(action)}')
print(f' Completed Team {str(team.team_name)}.')
except ContentType.DoesNotExist:
# DB is new so no content types. no migration to be done.
pass
print(' Permission Migration Actions Complete.')
class Migration(migrations.Migration):
dependencies = [
('access', '0008_alter_tenant_options_alter_entity_organization_and_more'),
]
operations = [
migrations.RunPython(add_tenancy_permissions),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 5.1.9 on 2025-05-16 09:58
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0009_migrate_organization_permission_tenant'),
]
operations = [
migrations.CreateModel(
name='Company',
fields=[
('entity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.entity')),
('name', models.CharField(help_text='The name of this entity', max_length=80, verbose_name='Name')),
],
options={
'verbose_name': 'Company',
'verbose_name_plural': 'Companies',
'ordering': ['name'],
'sub_model_type': 'company',
},
bases=('access.entity',),
),
migrations.AlterField(
model_name='entity',
name='entity_type',
field=models.CharField(help_text='Type this entity is', max_length=30, verbose_name='Entity Type'),
),
migrations.AlterField(
model_name='person',
name='dob',
field=models.DateField(blank=True, help_text='The Persons Date of Birth (DOB)', null=True, verbose_name='DOB'),
),
migrations.AlterField(
model_name='person',
name='m_name',
field=models.CharField(blank=True, help_text='The persons middle name(s)', max_length=100, null=True, verbose_name='Middle Name(s)'),
),
]

View File

@ -0,0 +1,110 @@
# Generated by Django 5.1.9 on 2025-06-06 01:41
import access.models.tenancy_abstract
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0010_company_alter_entity_entity_type_alter_person_dob_and_more"),
("core", "0028_delete_history"),
]
operations = [
migrations.RemoveField(
model_name="team",
name="is_global",
),
migrations.AlterField(
model_name="team",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="team",
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="TeamAuditHistory",
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.team",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team History",
"verbose_name_plural": "Team Histories",
"db_table": "access_team_audithistory",
"managed": True,
},
bases=("core.centurionaudit", models.Model),
),
migrations.CreateModel(
name="TeamCenturionModelNote",
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.team",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team Note",
"verbose_name_plural": "Team Notes",
"db_table": "access_team_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote", models.Model),
)
]

View File

@ -0,0 +1,102 @@
# Generated by Django 5.1.9 on 2025-06-06 01:43
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0011_remove_team_is_global_model_notes_and_more"),
("core", "0028_delete_history"),
]
operations = [
migrations.AddField(
model_name="teamusers",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="teamusers",
name="id",
field=models.AutoField(
help_text="ID of the item",
primary_key=True,
serialize=False,
unique=True,
verbose_name="ID",
),
),
migrations.CreateModel(
name="TeamUsersAuditHistory",
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.teamusers",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team User History",
"verbose_name_plural": "Team User Histories",
"db_table": "access_teamusers_audithistory",
"managed": True,
},
bases=("core.centurionaudit", models.Model),
),
migrations.CreateModel(
name="TeamUsersCenturionModelNote",
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.teamusers",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team User Note",
"verbose_name_plural": "Team User Notes",
"db_table": "access_teamusers_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote", models.Model),
),
]

View File

@ -0,0 +1,16 @@
# Generated by Django 5.1.9 on 2025-06-06 05:22
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("access", "0012_teamusers_model_notes_alter_teamusers_id_and_more"),
]
operations = [
migrations.DeleteModel(
name="TeamUsersAuditHistory",
),
]

View File

@ -0,0 +1,16 @@
# Generated by Django 5.1.9 on 2025-06-07 09:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("access", "0013_delete_teamusersaudithistory"),
]
operations = [
migrations.DeleteModel(
name="TeamUsersCenturionModelNote",
),
]

View File

@ -0,0 +1,75 @@
# Generated by Django 5.1.9 on 2025-06-07 10:10
import access.models.team
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0014_delete_teamuserscenturionmodelnote"),
]
operations = [
migrations.RemoveField(
model_name="teamcenturionmodelnote",
name="centurionmodelnote_ptr",
),
migrations.RemoveField(
model_name="teamcenturionmodelnote",
name="model",
),
migrations.RemoveField(
model_name="teamusers",
name="model_notes",
),
migrations.AddField(
model_name="team",
name="is_global",
field=models.BooleanField(
default=False,
help_text="Is this a global object?",
verbose_name="Global Object",
),
),
migrations.AlterField(
model_name="team",
name="model_notes",
field=models.TextField(
blank=True,
default=None,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="team",
name="organization",
field=models.ForeignKey(
help_text="Tenant this belongs to",
on_delete=django.db.models.deletion.CASCADE,
to="access.tenant",
validators=[access.models.team.Team.validatate_organization_exists],
verbose_name="Tenant",
),
),
migrations.AlterField(
model_name="teamusers",
name="id",
field=models.AutoField(
help_text="ID of this Team User",
primary_key=True,
serialize=False,
unique=True,
verbose_name="ID",
),
),
migrations.DeleteModel(
name="TeamAuditHistory",
),
migrations.DeleteModel(
name="TeamCenturionModelNote",
),
]

View File

@ -0,0 +1,112 @@
# Generated by Django 5.1.9 on 2025-06-08 04:18
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"access",
"0015_remove_teamcenturionmodelnote_centurionmodelnote_ptr_and_more",
),
("core", "0031_remove_ticketcategory_is_global_and_more"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name="tenant",
name="slug",
),
migrations.AlterField(
model_name="tenant",
name="manager",
field=models.ForeignKey(
blank=True,
help_text="Manager for this Tenancy",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
verbose_name="Manager",
),
),
migrations.AlterField(
model_name="tenant",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.CreateModel(
name="TenantAuditHistory",
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.tenant",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Tenant History",
"verbose_name_plural": "Tenant Histories",
"db_table": "access_tenant_audithistory",
"managed": True,
},
bases=("core.centurionaudit", models.Model),
),
migrations.CreateModel(
name="TenantCenturionModelNote",
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.tenant",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Tenant Note",
"verbose_name_plural": "Tenant Notes",
"db_table": "access_tenant_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote", models.Model),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 5.1.9 on 2025-06-17 07:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("access", "0016_remove_tenant_slug_alter_tenant_manager_and_more"),
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
]
operations = [
migrations.DeleteModel(
name="EntityHistory",
),
migrations.DeleteModel(
name="EntityNotes",
),
]

View File

@ -0,0 +1,253 @@
# Generated by Django 5.1.9 on 2025-06-17 07:32
import access.models.tenancy_abstract
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0017_remove_entitynotes_model_and_more"),
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
]
operations = [
migrations.RemoveField(
model_name="entity",
name="is_global",
),
migrations.AlterField(
model_name="entity",
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="entity",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="entity",
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="ContactAuditHistory",
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="access.contact",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Contact History",
"verbose_name_plural": "Contact Histories",
"db_table": "access_contact_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="ContactCenturionModelNote",
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.contact",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Contact Note",
"verbose_name_plural": "Contact Notes",
"db_table": "access_contact_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
migrations.CreateModel(
name="EntityAuditHistory",
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.entity",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Entity History",
"verbose_name_plural": "Entity Histories",
"db_table": "access_entity_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="EntityCenturionModelNote",
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.entity",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Entity Note",
"verbose_name_plural": "Entity Notes",
"db_table": "access_entity_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
migrations.CreateModel(
name="PersonAuditHistory",
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="access.person",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Person History",
"verbose_name_plural": "Person Histories",
"db_table": "access_person_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="PersonCenturionModelNote",
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.person",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Person Note",
"verbose_name_plural": "Person Notes",
"db_table": "access_person_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
]

View File

@ -1,438 +0,0 @@
from django.contrib.auth.mixins import AccessMixin, PermissionRequiredMixin
from django.contrib.auth.models import Group
from django.core.exceptions import PermissionDenied
from django.utils.functional import cached_property
from access.models.organization import Organization
from access.models.team import Team
class OrganizationMixin():
"""Base Organization class"""
parent_model: str = None
""" Parent Model
This attribute defines the parent model for the model in question. The parent model when defined
will be used as the object to obtain the permissions from.
"""
parent_model_pk_kwarg: str = 'pk'
"""Parent Model kwarg
This value is used to define the kwarg that is used as the parent objects primary key (pk).
"""
request = None
user_groups = []
def get_parent_obj(self):
""" Get the Parent Model Object
Use in views where the the model has no organization and the organization should be fetched from the parent model.
Requires attribute `parent_model` within the view with the value of the parent's model class
Returns:
parent_model (Model): with PK from kwargs['pk']
"""
return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])
def object_organization(self) -> int:
id = None
if hasattr(self, '_object_organization'):
return int(self._object_organization)
try:
if hasattr(self, 'get_queryset'):
self.get_queryset()
if self.parent_model:
obj = self.get_parent_obj()
id = obj.get_organization().id
if obj.is_global:
id = 0
if hasattr(self, 'get_object') and id is None:
obj = self.get_object()
id = obj.get_organization().id
if hasattr(obj, 'is_global'):
if obj.is_global:
id = 0
if hasattr(self, 'instance') and id is None: # Form Instance
id = self.instance.get_organization()
except AttributeError:
if self.request.method == 'POST':
if self.request.POST.get("organization", ""):
id = int(self.request.POST.get("organization", ""))
for field in self.request.POST.dict(): # cater for fields prefixed '<prefix>-<field name>'
a_field = str(field).split('-')
if len(a_field) == 2:
if a_field[1] == 'organization':
id = int(self.request.POST.get(field))
except:
pass
if id is not None:
self._object_organization = id
return id
def is_member(self, organization: int) -> bool:
"""Returns true if the current user is a member of the organization
iterates over the user_organizations list and returns true if the user is a member
Returns:
bool: _description_
"""
is_member = False
if organization is None:
return False
if int(organization) in self.user_organizations():
is_member = True
return is_member
def get_permission_required(self):
"""
Override of 'PermissionRequiredMixin' method so that this mixin can obtain the required permission.
"""
if not hasattr(self, 'permission_required'):
return []
if self.permission_required is None:
raise ImproperlyConfigured(
f"{self.__class__.__name__} is missing the "
f"permission_required attribute. Define "
f"{self.__class__.__name__}.permission_required, or override "
f"{self.__class__.__name__}.get_permission_required()."
)
if isinstance(self.permission_required, str):
perms = (self.permission_required,)
else:
perms = self.permission_required
return perms
@cached_property
def is_manager(self) -> bool:
""" Returns true if the current user is a member of the organization"""
is_manager = False
return is_manager
def user_organizations(self) -> list():
"""Current Users organizations
Fetches the Organizations the user is apart of.
Get All groups the user is part of, fetch the associated team,
iterate over the results adding the organization ID to a list to be returned.
Returns:
_type_: User Organizations.
"""
user_organizations = []
if hasattr(self, '_user_organizations'):
return self._user_organizations
teams = Team.objects
for group in self.request.user.groups.all():
team = teams.get(pk=group.id)
self.user_groups = self.user_groups + [group.id]
user_organizations = user_organizations + [team.organization.id]
if len(user_organizations) > 0:
self._user_organizations = user_organizations
return user_organizations
# ToDo: Ensure that the group has access to item
def has_organization_permission(self, organization: int = None, permissions_required: list = None) -> bool:
""" Check if user has permission within organization.
Args:
organization (int, optional): Organization to check. Defaults to None.
permissions_required (list, optional): if doing object level permissions, pass in required permission. Defaults to None.
Returns:
bool: True for yes.
"""
has_permission = False
if permissions_required is None:
permissions_required = self.get_permission_required()
if not organization:
organization = self.object_organization()
else:
organization = int(organization)
if self.is_member(organization) or organization == 0:
groups = Group.objects.filter(pk__in=self.user_groups)
for group in groups:
team = Team.objects.filter(pk=group.id)
team = team.values('organization_id').get()
for permission in group.permissions.values('content_type__app_label', 'codename').all():
assembled_permission = str(permission["content_type__app_label"]) + '.' + str(permission["codename"])
if assembled_permission in permissions_required and (team['organization_id'] == organization or organization == 0):
return True
return has_permission
def permission_check(self, request, permissions_required: list = None) -> bool:
self.request = request
if permissions_required:
self.permission_required = permissions_required
organization_manager_models = [
'access.organization',
'access.team',
'access.teamusers',
]
is_organization_manager = False
queryset = None
if hasattr(self, 'get_queryset'):
queryset = self.get_queryset()
obj = None
if hasattr(self, 'get_object'):
try:
obj = self.get_object()
except:
pass
if hasattr(self, 'model'):
if self.model._meta.label_lower in organization_manager_models:
organization = Organization.objects.get(pk=self.object_organization())
if organization.manager == request.user:
is_organization_manager = True
return True
if request.user.is_superuser:
return True
if permissions_required:
perms = permissions_required
else:
perms = self.get_permission_required()
if self.has_organization_permission(permissions_required = perms):
return True
if self.request.user.has_perms(perms) and str(self.request.method).lower() == 'get':
if len(self.kwargs) == 0 or (len(self.kwargs) == 1 and 'ticket_type' in self.kwargs):
return True
for required_permission in self.permission_required:
if required_permission.replace(
'view_', ''
) == 'access.organization' and len(self.kwargs) == 0:
return True
return False
class OrganizationPermission(AccessMixin, OrganizationMixin):
"""## Permission Checking
The base django permissions have not been modified with this app providing Multi-Tenancy. This is done by a mixin, that checks if the item is apart of an organization, if it is; confirmation is made that the user is part of the same organization and as long as they have the correct permission within the organization, access is granted.
### How it works
The overall permissions system of django has not been modified with it remaining fully functional. The multi-tenancy has been setup based off of an organization with teams. A team to the underlying django system is an extension of the django auth group and for every team created a django auth group is created. THe group name is set using the following format: `<organization>_<team name>` and contains underscores `_` instead of spaces.
A User who is added to an team as a "Manager" can modify the team members or if they have permission `access.change_team` which also allows the changing of team permissions. Modification of an organization can be done by the django administrator (super user) or any user with permission `access._change_organization`.
Items can be set as `Global`, meaning that all users who have the correct permission regardless of organization will be able to take action against the object.
Permissions that can be modified for a team have been limited to application permissions only unless adjust the permissions from the django admin site.
### Multi-Tenancy workflow
The workflow is conducted as part of the view and has the following flow:
1. Checks if user is member of organization the object the action is being performed on. Will also return true if the object has field `is_global` set to `true`.
1. Fetches all teams the user is part of.
1. obtains all permissions that are linked to the team.
1. checks if user has the required permission for the action.
1. confirms that the team the permission came from is part of the same organization as the object the action is being conducted on.
1. ONLY on success of the above items, grants access.
"""
permission_required: list = []
""" Permission required for the view
Not specifying this property adjusts the permission check logic so that you can
use the `permission_check()` function directly.
An example of a get request....
``` py
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
if not self.permission_check(request, [ 'access.view_organization' ]):
raise PermissionDenied('You are not part of this organization')
return super().get(request, *args, **kwargs)
```
this example details manual usage of the `permission_check()` function for a get request.
"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
if len(self.permission_required) == 0:
if hasattr(self, 'get_dynamic_permissions'):
self.permission_required = self.get_dynamic_permissions()
if len(self.permission_required) > 0:
non_organization_models = [
'TaskResult'
]
if hasattr(self, 'model'):
if hasattr(self.model, '__name__'):
if self.model.__name__ in non_organization_models:
if hasattr(self, 'get_object'):
self.get_object()
perms = self.get_permission_required()
if not self.request.user.has_perms(perms):
return self.handle_no_permission()
return super().dispatch(self.request, *args, **kwargs)
if not self.permission_check(request):
raise PermissionDenied('You are not part of this organization')
return super().dispatch(self.request, *args, **kwargs)

View File

@ -1,8 +1,10 @@
from django.contrib.auth.models import User, Group
import django
from django.db import models
from access.models.organization import Organization
from access.models.team import Team
from access.models.tenant import Tenant as Organization
User = django.contrib.auth.get_user_model()
@ -89,7 +91,7 @@ class OrganizationMixin:
self._obj_organization = obj.organization
elif str(self.model._meta.verbose_name).lower() == 'organization':
elif str(self.model._meta.verbose_name).lower() == 'tenant':
self._obj_organization = obj
@ -130,7 +132,7 @@ class OrganizationMixin:
parent_model (Model): with PK from kwargs['pk']
"""
return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])
return self.get_parent_model().objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])

View File

@ -1,23 +1,21 @@
import traceback
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import exceptions
from rest_framework.permissions import DjangoObjectPermissions
from access.models.tenancy import Organization, TenancyObject
from access.models.tenancy import Tenant
from core import exceptions as centurion_exceptions
from core.mixins.centurion import Centurion
class OrganizationPermissionMixin(
DjangoObjectPermissions,
):
"""Organization Permission Mixin
"""Tenant Permission Mixin
This class is to be used as the permission class for API `Views`/`ViewSets`.
In combination with the `OrganizationPermissionsMixin`, permission checking
In combination with the `TenantPermissionsMixin`, permission checking
will be done to ensure the user has the correct permissions to perform the
CRUD operation.
@ -60,11 +58,11 @@ class OrganizationPermissionMixin(
if hasattr(view, 'model'):
self._is_tenancy_model = issubclass(view.model, TenancyObject)
self._is_tenancy_model = issubclass(view.model, Centurion)
if view.get_parent_model():
self._is_tenancy_model = issubclass(view.get_parent_model(), TenancyObject)
self._is_tenancy_model = issubclass(view.get_parent_model(), Centurion)
return self._is_tenancy_model
@ -113,6 +111,12 @@ class OrganizationPermissionMixin(
raise centurion_exceptions.NotAuthenticated()
if request.method not in view.allowed_methods:
raise centurion_exceptions.MethodNotAllowed(method = request.method)
try:
if (
@ -156,17 +160,12 @@ class OrganizationPermissionMixin(
has_permission_required: bool = permission_required in user_permissions
if request.method not in view.allowed_methods:
raise centurion_exceptions.MethodNotAllowed(method = request.method)
elif not has_permission_required and not request.user.is_superuser:
if not has_permission_required and not request.user.is_superuser:
raise centurion_exceptions.PermissionDenied()
obj_organization: Organization = view.get_obj_organization(
obj_organization: Tenant = view.get_obj_organization(
request = request
)

View File

@ -1,2 +1,5 @@
from . import contact
from . import person
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

View File

@ -0,0 +1,97 @@
from django.db import models
from access.models.entity import Entity
class Company(
Entity
):
# This model is intended to be called `Organization`, however at the time of
# creation this was not possible as Tenant (ne Organization) still has
# references in code to `organization` witch clashes with the intended name of
# this model.
_is_submodel = True
documentation = ''
class Meta:
ordering = [
'name',
]
sub_model_type = 'company'
verbose_name = 'Company'
verbose_name_plural = 'Companies'
name = models.CharField(
blank = False,
help_text = 'The name of this entity',
max_length = 80,
unique = False,
verbose_name = 'Name'
)
def __str__(self) -> str:
return self.name
page_layout: dict = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'name',
],
"right": [
'model_notes',
'created',
'modified',
]
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Tickets",
"slug": "tickets",
"sections": [
{
"layout": "table",
"field": "tickets",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
},
]
table_fields: list = [
'name',
'organization',
'created',
]

View File

@ -8,6 +8,10 @@ class Contact(
Person
):
documentation = ''
_is_submodel = True
class Meta:
@ -42,10 +46,6 @@ class Contact(
return self.f_name + ' ' + self.l_name
documentation = ''
history_model_name = 'contact'
page_layout: list = [
{
"name": "Details",

View File

@ -1,18 +1,23 @@
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.lib.feature_not_used import FeatureNotUsed
from core.models.centurion import CenturionModel
class Entity(
TenancyObject
CenturionModel
):
model_tag = 'entity'
documentation = ''
kb_model_name = 'entity'
url_model_name = 'entity'
class Meta:
@ -29,31 +34,20 @@ class Entity(
verbose_name_plural = 'Entities'
id = models.AutoField(
blank=False,
help_text = 'Primary key of the entry',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
entity_type = models.CharField(
blank = False,
default = Meta.verbose_name.lower(),
help_text = 'Type this entity is',
max_length = 30,
unique = False,
verbose_name = 'Entity Type'
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
def __str__(self) -> str:
related_model = self.get_related_model()
if related_model is None:
@ -64,22 +58,8 @@ class Entity(
return str( related_model )
# app_namespace = 'access'
history_app_label = 'access'
history_model_name = 'entity'
kb_model_name = 'entity'
note_basename = '_api_v2_entity_note'
documentation = ''
page_layout: dict = []
table_fields: list = [
'organization',
'entity_type',
@ -89,6 +69,23 @@ class Entity(
]
def clean_fields(self, exclude = None ):
related_model = self.get_related_model()
if related_model is None:
related_model = self
if self.entity_type != str(related_model._meta.verbose_name).lower().replace(' ', '_'):
self.entity_type = str(related_model._meta.verbose_name).lower().replace(' ', '_')
super().clean_fields( exclude = exclude )
def get_related_field_name(self) -> str:
meta = getattr(self, '_meta')
@ -101,13 +98,12 @@ class Entity(
if getattr(self, related_object.name, None):
if(
if(
not str(related_object.name).endswith('history')
and not str(related_object.name).endswith('notes')
):
return related_object.name
break
return ''
@ -145,105 +141,3 @@ class Entity(
return related_model
def get_url_kwargs(self) -> dict:
model = self.get_related_model()
if len(self._meta.parents) == 0 and model is None:
return {
'pk': self.id
}
if model is None:
model = self
kwargs = {
'entity_model': str(model._meta.verbose_name).lower().replace(' ', '_'),
}
if model.pk:
kwargs.update({
'pk': model.id
})
return kwargs
def get_url( self, request = None ) -> str:
"""Fetch the models URL
If URL kwargs are required to generate the URL, define a `get_url_kwargs` that returns them.
Args:
request (object, optional): The request object that was made by the end user. Defaults to None.
Returns:
str: Canonical URL of the model if the `request` object was provided. Otherwise the relative URL.
"""
model = None
if getattr(self, 'get_related_model', None):
model = self.get_related_model()
if model is None:
model = self
sub_entity = ''
if model._meta.model_name != 'entity':
sub_entity = '_sub'
kwargs = self.get_url_kwargs()
view = 'list'
if 'pk' in kwargs:
view = 'detail'
if request:
return reverse(f"v2:" + model.get_app_namespace() + f"_api_v2_entity" + sub_entity + "-" + view, request=request, kwargs = kwargs )
return reverse(f"v2:" + model.get_app_namespace() + f"_api_v2_entity" + sub_entity + "-" + view, kwargs = 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.entity_type != str(related_model._meta.verbose_name).lower().replace(' ', '_'):
self.entity_type = str(related_model._meta.verbose_name).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 access.models.entity_history import EntityHistory
history = super().save_history(
before = before,
after = after,
history_model = EntityHistory
)
return history

View File

@ -1,155 +1 @@
from django.db import models
from django.contrib.auth.models import User
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField,
AutoSlugField
)
from core.mixin.history_save import SaveHistory
class Organization(SaveHistory):
class Meta:
verbose_name = "Organization"
verbose_name_plural = "Organizations"
ordering = ['name']
def save(self, *args, **kwargs):
if self.slug == '_':
self.slug = self.name.lower().replace(' ', '_')
super().save(*args, **kwargs)
id = models.AutoField(
blank=False,
help_text = 'ID of this item',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
name = models.CharField(
blank = False,
help_text = 'Name of this Organization',
max_length = 50,
unique = True,
verbose_name = 'Name'
)
manager = models.ForeignKey(
User,
blank = False,
help_text = 'Manager for this organization',
null = True,
on_delete=models.SET_NULL,
verbose_name = 'Manager'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null= True,
verbose_name = 'Notes',
)
slug = AutoSlugField()
created = AutoCreatedField()
modified = AutoLastModifiedField()
def get_organization(self):
return self
def __int__(self):
return self.id
def __str__(self):
return self.name
table_fields: list = [
'nbsp',
'name',
'created',
'modified',
'nbsp'
]
page_layout: list = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'manager',
'created',
'modified',
],
"right": [
'model_notes',
]
}
]
},
{
"name": "Teams",
"slug": "teams",
"sections": [
{
"layout": "table",
"field": "teams"
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
}
]
def get_url( self, request = None ) -> str:
if request:
return reverse("v2:_api_v2_organization-detail", request=request, kwargs={'pk': self.id})
return reverse("v2:_api_v2_organization-detail", kwargs={'pk': self.id})
def save_history(self, before: dict, after: dict) -> bool:
from access.models.organization_history import OrganizationHistory
history = super().save_history(
before = before,
after = after,
history_model = OrganizationHistory
)
return history
from .tenant import Tenant as Organization # pylint: disable=W0611:unused-import

View File

@ -2,7 +2,7 @@ from django.db import models
from core.models.model_history import ModelHistory
from access.models.organization import Organization
from access.models.tenant import Tenant
@ -23,7 +23,7 @@ class OrganizationHistory(
model = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Model this note belongs to',
null = False,
@ -46,8 +46,8 @@ class OrganizationHistory(
model = None
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
model = OrganizationBaseSerializer(self.model, context = serializer_context)
model = TenantBaseSerializer(self.model, context = serializer_context)
return model

View File

@ -1,6 +1,6 @@
from django.db import models
from access.models.organization import Organization
from access.models.tenant import Tenant
from core.models.model_notes import ModelNotes
@ -23,7 +23,7 @@ class OrganizationNotes(
model = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Model this note belongs to',
null = False,

View File

@ -10,6 +10,10 @@ class Person(
Entity
):
_is_submodel = True
documentation = ''
class Meta:
@ -36,7 +40,6 @@ class Person(
m_name = models.CharField(
blank = True,
default = None,
help_text = 'The persons middle name(s)',
max_length = 100,
null = True,
@ -54,7 +57,6 @@ class Person(
dob = models.DateField(
blank = True,
default = None,
help_text = 'The Persons Date of Birth (DOB)',
null = True,
unique = False,
@ -65,10 +67,6 @@ class Person(
return self.f_name + ' ' + self.l_name + f' (DOB: {self.dob})'
documentation = ''
history_model_name = 'person'
page_layout: dict = []
table_fields: list = [
@ -104,7 +102,7 @@ class Person(
for entry in duplicate_entry:
if(
entry.f_name == self.f_name
and entry.m_name == self.m_name
@ -114,8 +112,8 @@ class Person(
raise ValidationError(
detail = {
'dob': f'Person {self.f_name} {self.l_name} already exists with this birthday {entry.dob}'
'dob': f'Person {self.f_name} {self.l_name}' \
f'already exists with this birthday {entry.dob}'
},
code = 'duplicate_person_on_dob'
)

View File

@ -130,6 +130,37 @@ class Role(
]
_permissions: list[ Permission ] = None
_permissions_int: list[ int ] = None
def get_permissions(self, as_int_list = False ):
if self._permissions is None:
permissions = []
permissions_int = []
for permission in self.permissions: # pylint: disable=E1133:not-an-iterable
if permission in _permissions:
continue
permissions += [ permission ]
permissions_int += [ permission.id ]
self._permissions = permissions
self._permissions_int = permissions_int
if as_int_list:
return self._permissions_int
return self._permissions_int
def save_history(self, before: dict, after: dict) -> bool:
from access.models.role_history import RoleHistory

View File

@ -8,7 +8,7 @@ from access.fields import (
AutoLastModifiedField
)
from access.models.organization import Organization
from access.models.tenant import Tenant
from access.models.tenancy import TenancyObject
from core import exceptions as centurion_exceptions
@ -55,13 +55,13 @@ class Team(Group, TenancyObject):
)
organization = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Organization this belongs to',
help_text = 'Tenant this belongs to',
null = False,
on_delete = models.CASCADE,
validators = [validatate_organization_exists],
verbose_name = 'Organization'
verbose_name = 'Tenant'
)
created = AutoCreatedField()
@ -179,12 +179,12 @@ class Team(Group, TenancyObject):
def save_history(self, before: dict, after: dict) -> bool:
from access.models.team_history import TeamHistory
from access.models.team_history import TeamAuditHistory
history = super().save_history(
before = before,
after = after,
history_model = TeamHistory
history_model = TeamAuditHistory
)

View File

@ -1,6 +1,8 @@
import django
from django.conf import settings
from django.db import models
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group
from rest_framework.reverse import reverse
@ -9,11 +11,13 @@ from access.fields import (
AutoLastModifiedField
)
from access.models.organization import Organization
from access.models.tenant import Tenant
from access.models.team import Team
from core.lib.feature_not_used import FeatureNotUsed
from core.mixin.history_save import SaveHistory
from core.mixins.history_save import SaveHistory
User = django.contrib.auth.get_user_model()
@ -95,7 +99,7 @@ class TeamUsers(SaveHistory):
user.groups.remove(group)
def get_organization(self) -> Organization:
def get_organization(self) -> Tenant:
return self.team.organization

View File

@ -1,16 +1,14 @@
# from django.conf import settings
import logging
from django.db import models
# from django.contrib.auth.models import User, Group
from rest_framework.reverse import reverse
# from .fields import *
from access.models.organization import Organization
from access.models.tenant import Tenant
from core import exceptions as centurion_exceptions
from core.middleware.get_request import get_request
from core.mixin.history_save import SaveHistory
from core.mixins.history_save import SaveHistory
@ -53,6 +51,10 @@ class TenancyManager(models.Manager):
user_organizations: list(str()) = []
has_tenant_field = False
if getattr(self.model, 'organization', None) is not None:
has_tenant_field = True
if request:
@ -71,29 +73,33 @@ class TenancyManager(models.Manager):
if team.organization.id not in user_organizations:
if not user_organizations:
# if not user_organizations:
self.user_organizations = []
# self.user_organizations = []
user_organizations += [ team.organization.id ]
# if len(user_organizations) > 0 and not user.is_superuser and self.model.is_global is not None:
if len(user_organizations) > 0 and not user.is_superuser:
if getattr(self.model, 'is_global', False) is True:
if has_tenant_field:
return super().get_queryset().filter(
return super().get_queryset().select_related('organization').filter(
models.Q(organization__in=user_organizations)
|
models.Q(is_global = True)
)
else:
return super().get_queryset().filter(
models.Q(organization__in=user_organizations)
)
# return super().get_queryset().filter(
# models.Q(organization__in=user_organizations)
# )
return super().get_queryset().filter()
if has_tenant_field:
return super().get_queryset().select_related('organization')
return super().get_queryset()
@ -137,14 +143,14 @@ class TenancyObject(SaveHistory):
)
organization = models.ForeignKey(
Organization,
Tenant,
blank = False,
help_text = 'Organization this belongs to',
help_text = 'Tenancy this belongs to',
null = False,
on_delete = models.CASCADE,
related_name = '+',
validators = [validatate_organization_exists],
verbose_name = 'Organization'
verbose_name = 'Tenant'
)
is_global = models.BooleanField(
@ -162,7 +168,7 @@ class TenancyObject(SaveHistory):
verbose_name = 'Notes',
)
def get_organization(self) -> Organization:
def get_organization(self) -> Tenant:
return self.organization
app_namespace: str = None
@ -193,6 +199,16 @@ class TenancyObject(SaveHistory):
only be used when there is model inheritence.
"""
_log: logging.Logger = None
def get_log(self):
if self._log is None:
self._log = logging.getLogger('centurion.' + self._meta.app_label)
return self._log
page_layout: list = None
note_basename: str = None
@ -220,7 +236,7 @@ class TenancyObject(SaveHistory):
if self.app_namespace:
app_namespace = self.app_namespace + ':'
app_namespace = self.app_namespace
return str(app_namespace)
@ -239,12 +255,17 @@ class TenancyObject(SaveHistory):
model_name = str(self._meta.verbose_name.lower()).replace(' ', '_')
namespace = f'v2'
if self.get_app_namespace():
namespace = namespace + ':' + self.get_app_namespace()
if request:
return reverse(f"v2:" + self.get_app_namespace() + f"_api_v2_{model_name}-detail", request=request, kwargs = self.get_url_kwargs() )
return reverse(f"{namespace}:_api_v2_{model_name}-detail", request=request, kwargs = self.get_url_kwargs() )
return reverse(f"v2:" + self.get_app_namespace() + f"_api_v2_{model_name}-detail", kwargs = self.get_url_kwargs() )
return reverse(f"{namespace}:_api_v2_{model_name}-detail", kwargs = self.get_url_kwargs() )
def get_url_kwargs(self) -> dict:
@ -282,7 +303,7 @@ class TenancyObject(SaveHistory):
raise centurion_exceptions.ValidationError(
detail = {
'organization': 'Organization is required'
'organization': 'Tenant is required'
},
code = 'required'
)

View File

@ -0,0 +1,126 @@
from django.core.exceptions import (
ValidationError,
)
from django.db import models
from access.models.tenancy import (
TenancyManager as TenancyManagerDepreciated
)
from access.models.tenant import Tenant
class TenancyManager(
models.Manager
):
"""Multi-Tennant Object Manager
This manager specifically caters for the multi-tenancy features of Centurion ERP.
"""
def get_queryset(self):
""" Fetch the data
When the model contains the user data, the query is filtered to their
and the globally defined Tenancy only.
Returns:
(queryset): **super user**: return unfiltered data.
(queryset): **not super user**: return data from the stored unique organizations.
"""
user = None # When CenturionUser in use
if hasattr(self.model, 'context'):
user = self.model.context['user']
has_tenant_field = False
if getattr(self.model, 'organization', None) is not None:
has_tenant_field = True
if user:
tenancies = user.get_tenancies(int_list = True)
if len(tenancies) > 0 and not request.user.is_superuser:
if has_tenant_field:
return super().get_queryset().select_related('organization').filter(
models.Q(organization__in = tenancies)
)
return super().get_queryset().filter()
if has_tenant_field:
return super().get_queryset().select_related('organization')
return super().get_queryset()
class TenancyAbstractModel(
models.Model,
):
""" Tenancy Model Abstract class.
This class is for inclusion within **every** model within Centurion ERP.
Provides the required fields, functions and methods for multi tennant objects.
Unless otherwise stated, **no** object within this class may be overridden.
Raises:
ValidationError: User failed to supply organization
"""
objects = TenancyManagerDepreciated()
""" ~~Multi-Tenant Manager~~
**Note:** ~~This manager relies upon the model class having `context['user']`
set. without a user the manager can not perform multi-tenant queries.~~
"""
class Meta:
abstract = True
def validatate_organization_exists(self):
"""Ensure that the user did provide an organization
Raises:
ValidationError: User failed to supply organization.
"""
if not self:
raise ValidationError(
code = 'required',
message = 'You must provide an organization'
)
organization = models.ForeignKey(
Tenant,
blank = False,
help_text = 'Tenant this belongs to',
null = False,
on_delete = models.CASCADE,
related_name = '+',
validators = [
validatate_organization_exists
],
verbose_name = 'Tenant'
)
def get_tenant(self) -> Tenant:
""" Return the models Tenancy
This model can be safely over-ridden as long as it returns the models
tenancy
"""
return self.organization

143
app/access/models/tenant.py Normal file
View File

@ -0,0 +1,143 @@
import django
from django.conf import settings
from django.db import models
from access.fields import (
AutoCreatedField,
AutoLastModifiedField,
)
from core.mixins.centurion import Centurion
User = django.contrib.auth.get_user_model()
class Tenant(
Centurion,
):
@property
def organization(self):
return self
model_tag = 'tenant'
class Meta:
verbose_name = "Tenant"
verbose_name_plural = "Tenants"
ordering = [
'name'
]
id = models.AutoField(
blank = False,
help_text = 'ID of this item',
primary_key = True,
unique = True,
verbose_name = 'ID'
)
name = models.CharField(
blank = False,
help_text = 'Name of this Tenancy',
max_length = 50,
unique = True,
verbose_name = 'Name'
)
manager = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank = True,
help_text = 'Manager for this Tenancy',
null = True,
on_delete = models.PROTECT,
verbose_name = 'Manager'
)
model_notes = models.TextField(
blank = True,
help_text = 'Tid bits of information',
null = True,
verbose_name = 'Notes',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
def __int__(self):
return self.id
def __str__(self):
return self.name
def get_organization(self):
return self
table_fields: list = [
'nbsp',
'name',
'created',
'modified',
'nbsp'
]
page_layout: list = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'name',
'manager',
'created',
'modified',
],
"right": [
'model_notes',
]
}
]
},
{
"name": "Teams",
"slug": "teams",
"sections": [
{
"layout": "table",
"field": "teams"
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
}
]
Organization = Tenant

View File

@ -0,0 +1,197 @@
from django.contrib.auth.models import Permission, User
from django.core.exceptions import PermissionDenied
from access.models.tenant import Tenant
class CenturionUser(
User,
):
"""Centurion User
A Multi-Tenant User wirh permission Checking.
ToDo:
- Add to Roles user field `related_name = roles`
- Add to Roles group field `related_name = roles`
# - have group lookup prefetch related roles__permissions
- have user lookup prefetch related roles__permissions and groups__roles__permissions
Args:
User (Model): Django Base User
"""
_tenancies: list[Tenant] = None
_tenancies_int: list[int] = None
_permissions: list[Permission] = None
_permissions_by_tenancy: dict[ str, list[ Permission ] ] = None
"""Permissions by Tenancy
`{ 'tenancy_{id}': [ Permission ] }`
"""
# EMAIL_FIELD = 'email' # Update contact email field name so it's different to the user model.
# REQUIRED_FIELDS = [
# EMAIL_FIELD,
# 'f_name',
# 'l_name',
# ]
class Meta:
abstract = False
proxy = True # User will be linked to Employee/Customer entity via related_name from the entity.
# ToDo: refactory Employee/Customer to inherit from a new model. entity_user
verbose_name = 'Centurion User'
verbose_name_plural = 'Centurion Users'
def get_full_name(self) -> str:
return f'{self.entity_user.f_name} {self.entity_user.l_name}'
def get_group_permissions(self, tenancy: bool = True) -> dict[ str, list[ Permission ] ] | list[ Permission ]:
""" Get the Users Permissions
Args:
tenancy (bool, optional): Return permission in list. Defaults to True.
Returns:
dict[ str, list[ Permission ] ]: Permissions listed by tenancy
list[ Permission ]: All Permissions
"""
for group in self.groups: # pylint: disable=E1133:not-an-iterable
for role in group.roles:
pass
# role.get_permissions()
def get_permissions(self, tenancy: bool = True) -> dict[ str, list[ Permission ] ] | list[ Permission ]:
""" Get the Users Permissions
Args:
tenancy (bool, optional): Return permission in list. Defaults to True.
Returns:
dict[ str, list[ Permission ] ]: Permissions listed by tenancy
list[ Permission ]: All Permissions
"""
# also get group permissions. self.get_group_permissions()
for role in self.roles:
pass
# role.get_permissions()
# also populate `self._tenancies` and `self._tenancies_int`
return []
def get_short_name() -> str:
return self.entity_user.f_name
def get_tenancies(self, int_list = False) -> list[ Tenant ] | list[ int ]:
"""Get the Tenancies the user is in.
Args:
int_list (bool, optional): Return Tenancy list as int values. Defaults to False.
Returns:
list[ Tenant ] | list[ int ]: All Tenancies the user is in.
"""
if self._tenancies is None:
if self._permissions is None:
self.get_permissions
tenancies: list = []
tenancies_int: list = []
for role in self.roles:
if role.organization in tenancies:
continue
tenancies += [ role.organization ]
tenancies_int += [ role.organization.id ]
self._tenancies = tenancies
self._tenancies_int = tenancies_int
if as_int_list:
return self._tenancies_int
return self._tenancies
def has_module_perms(self, app_label): # is this needed?
# if has app_label in perms
raise PermissionDenied
def has_perm(self, permission: Permission, obj = None, tenancy: Tenant = None) -> bool:
if(
obj is None
and tenancy is None
):
raise ValueError('Both obj and tenancy cant be None')
if tenancy is None:
tenancy = obj.organization
# if self.has_tenancy_permission(perm, tenancy):
# for tenancy, permissions in self.get_permissions().items()
if tenancy is None:
raise ValueError('tenancy cant be None')
permissions = self.get_permissions()
if f'tenancy_{tenancy.id}' not in permissions:
raise PermissionDenied
for tenancy, permissions in self.get_permissions().items():
if(
tenancy == f'tenancy_{tenancy.id}'
and perm in permissions
):
return True
raise PermissionDenied
def has_perms(self, permission_list: list[ Permission ], obj = None, tenancy: Tenant = None):
for perm in perm_list:
self.has_perm( perm, obj )
return True

View 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 CompanyAuditHistory # 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 = 'CompanyAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = CompanyAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'CompanyAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View 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 ContactAuditHistory # 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 = 'ContactAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = ContactAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'ContactAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View 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 EntityAuditHistory # 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 = 'EntityAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = EntityAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'EntityAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View 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 PersonAuditHistory # 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 = 'PersonAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = PersonAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'PersonAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View 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 TenantAuditHistory # 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 = 'TenantAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = TenantAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'TenantAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View 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 CompanyCenturionModelNote # 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 = 'CompanyModelNoteModelSerializer')
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 = CompanyCenturionModelNote
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 = 'CompanyModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View 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 ContactCenturionModelNote # 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 = 'ContactModelNoteModelSerializer')
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 = ContactCenturionModelNote
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 = 'ContactModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View 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 EntityCenturionModelNote # 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 = 'EntityModelNoteModelSerializer')
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 = EntityCenturionModelNote
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 = 'EntityModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View 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 PersonCenturionModelNote # 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 = 'PersonModelNoteModelSerializer')
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 = PersonCenturionModelNote
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 = 'PersonModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View 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 TenantCenturionModelNote # 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 = 'TeamModelNoteModelSerializer')
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 = TenantCenturionModelNote
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 = 'TeamModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View File

@ -6,7 +6,7 @@ from access.models.entity import Entity
from api.serializers import common
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
@ -66,7 +66,6 @@ class ModelSerializer(
'entity_type',
'display_name',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
@ -87,4 +86,4 @@ class ModelSerializer(
class ViewSerializer(ModelSerializer):
"""Entity Base View Model"""
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -0,0 +1,69 @@
from drf_spectacular.utils import extend_schema_serializer
from access.models.company_base import Company
from access.serializers.entity import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import TenantBaseSerializer
class BaseSerializer(
BaseBaseSerializer,
):
pass
@extend_schema_serializer(component_name = 'CompanyEntityModelSerializer')
class ModelSerializer(
BaseSerializer,
BaseModelSerializer,
):
"""Company Model
This model inherits from the Entity base model.
"""
class Meta:
model = Company
fields = [
'id',
'entity_ptr_id',
'organization',
'entity_type',
'display_name',
'name',
'model_notes',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'entity_type',
'created',
'modified',
'_urls',
]
@extend_schema_serializer(component_name = 'CompanyEntityViewSerializer')
class ViewSerializer(
ModelSerializer,
):
"""Company View Model
This model inherits from the Entity base model.
"""
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -6,7 +6,7 @@ from access.serializers.entity_person import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
@ -46,7 +46,6 @@ class ModelSerializer(
'email',
'directory',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
@ -72,4 +71,4 @@ class ViewSerializer(
This model inherits from the Person model.
"""
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -1,41 +0,0 @@
from core.serializers.model_notes import (
ModelNoteBaseSerializer,
ModelNoteModelSerializer,
ModelNoteViewSerializer
)
from access.models.entity_notes import EntityNotes
class EntityNoteBaseSerializer(ModelNoteBaseSerializer):
pass
class EntityNoteModelSerializer(
ModelNoteModelSerializer
):
class Meta:
model = EntityNotes
fields = ModelNoteModelSerializer.Meta.fields + [
'model',
]
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
'model',
'content_type',
]
class EntityNoteViewSerializer(
ModelNoteViewSerializer,
EntityNoteModelSerializer,
):
pass

View File

@ -6,7 +6,7 @@ from access.serializers.entity import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
@ -44,7 +44,6 @@ class ModelSerializer(
'l_name',
'dob',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
@ -70,4 +69,4 @@ class ViewSerializer(
This model inherits from the Entity base model.
"""
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -2,15 +2,16 @@ from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models.organization import Organization
from access.models.tenant import Tenant
from app.serializers.user import UserBaseSerializer
from centurion.serializers.user import UserBaseSerializer
from core import fields as centurion_field
Organization = Tenant
class OrganizationBaseSerializer(serializers.ModelSerializer):
class TenantBaseSerializer(serializers.ModelSerializer):
display_name = serializers.SerializerMethodField('get_display_name')
@ -19,12 +20,12 @@ class OrganizationBaseSerializer(serializers.ModelSerializer):
return str( item )
url = serializers.HyperlinkedIdentityField(
view_name="v2:_api_v2_organization-detail", format="html"
view_name="v2:_api_tenant-detail", format="html"
)
class Meta:
model = Organization
model = Tenant
fields = [
'id',
@ -42,8 +43,8 @@ class OrganizationBaseSerializer(serializers.ModelSerializer):
class OrganizationModelSerializer(
OrganizationBaseSerializer
class TenantModelSerializer(
TenantBaseSerializer
):
_urls = serializers.SerializerMethodField('get_url')
@ -60,13 +61,13 @@ class OrganizationModelSerializer(
'model_pk': item.pk
}
),
'notes': reverse(
"v2:_api_v2_organization_note-list",
request=self._context['view'].request,
kwargs={
'model_id': item.pk
}
),
# 'notes': reverse(
# "v2:_api_v2_organization_note-list",
# request=self._context['view'].request,
# kwargs={
# 'model_id': item.pk
# }
# ),
'teams': reverse("v2:_api_v2_organization_team-list", request=self._context['view'].request, kwargs={'organization_id': item.pk}),
}
@ -74,7 +75,7 @@ class OrganizationModelSerializer(
class Meta:
model = Organization
model = Tenant
fields = '__all__'
@ -98,7 +99,7 @@ class OrganizationModelSerializer(
]
class OrganizationViewSerializer(OrganizationModelSerializer):
class TenantViewSerializer(TenantModelSerializer):
pass
manager = UserBaseSerializer(many=False, read_only = True)

View File

@ -1,48 +0,0 @@
from rest_framework import serializers
from access.models.organization_notes import OrganizationNotes
from api.serializers import common
from app.serializers.user import UserBaseSerializer
from core.serializers.model_notes import (
ModelNotes,
ModelNoteBaseSerializer,
ModelNoteModelSerializer,
ModelNoteViewSerializer
)
class OrganizationNoteBaseSerializer(ModelNoteBaseSerializer):
pass
class OrganizationNoteModelSerializer(
ModelNoteModelSerializer
):
class Meta:
model = OrganizationNotes
fields = ModelNoteModelSerializer.Meta.fields + [
'model',
]
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
'model',
'content_type',
]
class OrganizationNoteViewSerializer(
ModelNoteViewSerializer,
OrganizationNoteModelSerializer,
):
pass

View File

@ -5,11 +5,11 @@ from drf_spectacular.utils import extend_schema_serializer
from access.functions.permissions import permission_queryset
from access.models.role import Role
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
from api.serializers import common
from app.serializers.permission import PermissionBaseSerializer
from centurion.serializers.permission import PermissionBaseSerializer
@ -109,6 +109,6 @@ class ModelSerializer(
class ViewSerializer(ModelSerializer):
"""Role Base View Model"""
organization = OrganizationBaseSerializer( many=False, read_only=True )
organization = TenantBaseSerializer( many=False, read_only=True )
permissions = PermissionBaseSerializer( many=True, read_only=True )

View File

@ -1,48 +0,0 @@
from rest_framework import serializers
from access.models.role_notes import RoleNotes
from api.serializers import common
from app.serializers.user import UserBaseSerializer
from core.serializers.model_notes import (
ModelNotes,
ModelNoteBaseSerializer,
ModelNoteModelSerializer,
ModelNoteViewSerializer
)
class RoleNoteBaseSerializer(ModelNoteBaseSerializer):
pass
class RoleNoteModelSerializer(
ModelNoteModelSerializer
):
class Meta:
model = RoleNotes
fields = ModelNoteModelSerializer.Meta.fields + [
'model',
]
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
'model',
'content_type',
]
class RoleNoteViewSerializer(
ModelNoteViewSerializer,
RoleNoteModelSerializer,
):
pass

View File

@ -1,48 +0,0 @@
from rest_framework import serializers
from access.models.team_notes import TeamNotes
from api.serializers import common
from app.serializers.user import UserBaseSerializer
from core.serializers.model_notes import (
ModelNotes,
ModelNoteBaseSerializer,
ModelNoteModelSerializer,
ModelNoteViewSerializer
)
class TeamNoteBaseSerializer(ModelNoteBaseSerializer):
pass
class TeamNoteModelSerializer(
ModelNoteModelSerializer
):
class Meta:
model = TeamNotes
fields = ModelNoteModelSerializer.Meta.fields + [
'model',
]
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
'model',
'content_type',
]
class TeamNoteViewSerializer(
ModelNoteViewSerializer,
TeamNoteModelSerializer,
):
pass

View File

@ -1,12 +1,10 @@
from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models.team_user import TeamUsers
from api.serializers import common
from app.serializers.user import UserBaseSerializer
from centurion.serializers.user import UserBaseSerializer
@ -55,7 +53,7 @@ class TeamUserModelSerializer(
get_url = super().get_url( item = item )
del get_url['history']
# del get_url['history']
del get_url['knowledge_base']

View File

@ -7,9 +7,9 @@ from access.models.team import Team
from api.serializers import common
from access.functions.permissions import permission_queryset
from access.serializers.organization import OrganizationBaseSerializer
from access.serializers.organization import TenantBaseSerializer
from app.serializers.permission import Permission, PermissionBaseSerializer
from centurion.serializers.permission import PermissionBaseSerializer
from core import fields as centurion_field
@ -94,7 +94,6 @@ class TeamModelSerializer(
'model_notes',
'permissions',
'organization',
'is_global',
'created',
'modified',
'_urls',
@ -127,6 +126,6 @@ class TeamModelSerializer(
class TeamViewSerializer(TeamModelSerializer):
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)
permissions = PermissionBaseSerializer(many = True)

View File

@ -1,22 +0,0 @@
{% extends 'base.html.j2' %}
{% block content_header_icon %}{% endblock %}
{% block content %}
<table class="data">
<tr>
<th>Name</th>
<th>Created</th>
<th>Modified</th>
</tr>
{% for org in organization_list %}
<tr>
<td><a href="/organization/{{ org.id }}/">{{ org.name }}</a></td>
<td>{{ org.created }}</td>
<td>{{ org.modified }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -1,106 +0,0 @@
{% extends 'base.html.j2' %}
{% load markdown %}
{% block title %}Organization - {{ organization.name }}{% endblock %}
{% block content %}
<style>
form div .helptext {
background-color: rgb(0, 140, 255);
display: block;
}
.detail-view-field {
display:unset;
height: 30px;
line-height: 30px;
padding: 0px 20px 40px 20px;
}
.detail-view-field label {
display: inline-block;
font-weight: bold;
width: 200px;
margin: 10px;
/*padding: 10px;*/
height: 30px;
line-height: 30px;
}
.detail-view-field span {
display: inline-block;
width: 340px;
margin: 10px;
/*padding: 10px;*/
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
}
</style>
<div style="align-items:flex-start; align-content: center; display: flexbox; width: 100%">
<div style="display: inline; width: 40%; margin: 30px;">
<div class="detail-view-field">
<label>{{ form.name.label }}</label>
<span>{{ form.name.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.manager.label }}</label>
<span>{{ organization.manager }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.created.label }}</label>
<span>{{ form.created.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.modified.label }}</label>
<span>{{ form.modified.value }}</span>
</div>
</div>
<div style="display: inline; width: 40%; margin: 30px; text-align: left;">
<div>
<label style="font-weight: bold; width: 100%; border-bottom: 1px solid #ccc; display: block; text-align: inherit;">{{ form.model_notes.label }}</label>
<div style="display: inline-block; text-align: left;">{{ form.model_notes.value | markdown | safe }}</div>
</div>
</div>
<div style="display: block;">
<input type="button" value="<< Back" onclick="window.location='{% url 'Access:Organizations' %}';">
<input type="button" value="New Team" onclick="window.location='{% url 'Access:_team_add' organization.id %}';">
</div>
<hr />
<table>
<thead>
<tr>
<th>Team Name</th>
<th>Created</th>
<th>Modified</th>
</tr>
</thead>
{% for field in teams %}
<tr>
<td><a href="{% url 'Access:_team_view' organization_id=organization.id pk=field.id %}">{{ field.team_name }}</a></td>
<td>{{ field.created }}</td>
<td>{{ field.modified }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -1,48 +0,0 @@
{% extends 'base.html.j2' %}
{% block title %}Team - {{ team.team_name }}{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_div }}
<input style="display:unset;" type="submit" value="Submit">
</form>
<hr />
<input type="button" value="<< Back" onclick="window.location='{% url 'Access:_organization_view' pk=organization.id %}';">
<input type="button" value="Delete Team"
onclick="window.location='{% url 'Access:_team_delete' organization_id=organization.id pk=team.id %}';">
<input type="button" value="Assign User"
onclick="window.location='{% url 'Access:_team_user_add' organization_id=organization.id pk=team.id %}';">
{{ formset.management_form }}
<table id="formset" class="form">
<thead>
<tr>
<th>User</th>
<th>Manager</th>
<th>Created</th>
<th>Modified</th>
<th>&nbsp;</th>
</tr>
</thead>
{% for field in teamusers %}
<tr>
<td>{{ field.user }}</td>
<td><input type="checkbox" {% if field.manager %}checked{% endif %} disabled></td>
<td>{{ field.created }}</td>
<td>{{ field.modified }}</td>
<td><a
href="{% url 'Access:_team_user_delete' organization_id=organization.id team_id=field.team_id pk=field.id %}">Delete</a></a>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -1,32 +0,0 @@
class TenancyObject:
""" Tests for checking TenancyObject """
model = None
""" Model to be tested """
should_model_history_be_saved: bool = True
""" Should model history be saved.
By default this should always be 'True', however in special
circumstances, this may not be desired.
"""
# def test_history_save(self):
# """Confirm the desired intent for saving model history."""
# assert self.model.save_model_history == self.should_model_history_be_saved
# @pytest.mark.skip(reason="to be written")
# def test_edit_no_organization_fails(self):
# """ Devices must be assigned an organization
# Must not be able to edit an item without an organization
# """
# pass

View File

@ -0,0 +1,24 @@
import pytest
from access.models.company_base import Company
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Company
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_company import ModelSerializer
yield ModelSerializer

View File

@ -0,0 +1,72 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.functional.entity.test_functional_entity_metadata import (
EntityMetadataInheritedCases
)
class CompanyMetadataTestCases(
EntityMetadataInheritedCases,
):
add_data: dict = {
'name': 'Ian1'
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
model = Company
class CompanyMetadataInheritedCases(
CompanyMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
# self.url_kwargs = {
# 'model_name': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'model_name': self.model._meta.sub_model_type
# }
super().setUpTestData()
class CompanyMetadataTest(
CompanyMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,43 @@
import pytest
from access.tests.functional.entity.test_functional_entity_permission import (
EntityPermissionsAPIInheritedCases
)
class CompanyPermissionsAPITestCases(
EntityPermissionsAPIInheritedCases,
):
add_data: dict = {
'name': 'Ian1',
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
class CompanyPermissionsAPIInheritedCases(
CompanyPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
class CompanyPermissionsAPIPyTest(
CompanyPermissionsAPITestCases,
):
pass

View File

@ -0,0 +1,46 @@
import pytest
from rest_framework.exceptions import ValidationError
from access.tests.functional.entity.test_functional_entity_serializer import (
MockView,
EntitySerializerInheritedCases
)
class CompanySerializerTestCases(
EntitySerializerInheritedCases
):
parameterized_test_data: dict = {
"name": {
'will_create': False,
'exception_key': 'required'
},
}
valid_data: dict = {
'name': 'Ian',
}
"""Valid data used by serializer to create object"""
class CompanySerializerInheritedCases(
CompanySerializerTestCases,
):
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
class CompanySerializerPyTest(
CompanySerializerTestCases,
):
parameterized_test_data: dict = None

View File

@ -0,0 +1,58 @@
from django.test import TestCase
from access.models.company_base import Company
from access.tests.functional.entity.test_functional_entity_viewset import (
EntityViewSetInheritedCases
)
class ViewSetTestCases(
EntityViewSetInheritedCases,
):
add_data: dict = {
'name': 'Ian',
}
kwargs_create_item: dict = {
'name': 'Ian2',
}
kwargs_create_item_diff_org: dict = {
'name': 'Ian3',
}
model = Company
class CompanyViewSetInheritedCases(
ViewSetTestCases,
):
model = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
class CompanyViewSetTest(
ViewSetTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,24 @@
import pytest
from access.models.contact import Contact
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Contact
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_contact import ModelSerializer
yield ModelSerializer

View File

@ -1,60 +0,0 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_history import (
PersonHistoryInheritedCases
)
class ContactTestCases(
PersonHistoryInheritedCases,
):
field_name = 'model_notes'
kwargs_create_obj: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_delete_obj: dict = {
'email': 'ipweird@unit.test',
}
model = Contact
class ContactHistoryInheritedCases(
ContactTestCases,
):
model = None
"""Entity model to test"""
kwargs_create_obj: dict = None
kwargs_delete_obj: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_obj.update(
super().kwargs_create_obj
)
self.kwargs_delete_obj.update(
super().kwargs_delete_obj
)
super().setUpTestData()
class ContactHistoryTest(
ContactTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,65 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_metadata import (
PersonMetadataInheritedCases
)
class ContactMetadataTestCases(
PersonMetadataInheritedCases,
):
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
model = Contact
class ContactMetadataInheritedCases(
ContactMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
class ContactMetadataTest(
ContactMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,70 @@
import pytest
from access.tests.functional.person.test_functional_person_permission import (
PersonPermissionsAPIInheritedCases
)
class ContactPermissionsAPITestCases(
PersonPermissionsAPIInheritedCases,
):
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
class ContactPermissionsAPIInheritedCases(
ContactPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
# url_name = '_api_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'model_name': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'model_name': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class ContactPermissionsAPIPyTest(
ContactPermissionsAPITestCases,
):
pass

View File

@ -1,129 +1,46 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.serializers.entity_contact import (
Contact,
ModelSerializer
)
from access.tests.functional.person.test_functional_person_serializer import (
MockView,
PersonSerializerInheritedCases
)
class SerializerTestCases(
PersonSerializerInheritedCases,
class ContactSerializerTestCases(
PersonSerializerInheritedCases
):
duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicateone@unit.test',
parameterized_test_data: dict = {
"email": {
'will_create': False,
'exception_key': 'required'
}
}
kwargs_create_item: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item_duplicate_f_name_l_name_dob = {
'email': 'contactentityduplicatetwo@unit.test',
}
model = Contact
"""Model to test"""
create_model_serializer = ModelSerializer
"""Serializer to test"""
valid_data: dict = {
'email': 'ipweird@unit.test',
'email': 'contactentityduplicatetwo@unit.test',
}
def test_serializer_validation_no_email_exception(self):
"""Serializer Validation Check
Ensure that when creating with valid data and field email is missing
a validation error occurs.
"""
data = self.valid_data.copy()
del data['email']
with pytest.raises(ValidationError) as err:
serializer = self.create_model_serializer(
data = data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['email'][0] == 'required'
"""Valid data used by serializer to create object"""
class ContactSerializerInheritedCases(
SerializerTestCases,
ContactSerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
duplicate_f_name_l_name_dob: dict = None
""" Duplicate model serializer dict
used for testing for duplicate f_name, l_name and dob fields.
"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
kwargs_create_item_duplicate_f_name_l_name_dob: dict = None
"""model kwargs to create object
**None:** Ensure that the fields of sub-model to person do not match
`self.duplicate_f_name_l_name_dob`. if they do the wrong exception will be thrown.
used for testing for duplicate f_name, l_name and dob fields.
"""
model = None
"""Model to test"""
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.duplicate_f_name_l_name_dob.update(
super().duplicate_f_name_l_name_dob
)
self.kwargs_create_item_duplicate_f_name_l_name_dob.update(
super().kwargs_create_item_duplicate_f_name_l_name_dob
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.valid_data.update(
super().valid_data
)
super().setUpTestData()
class ContactSerializerTest(
SerializerTestCases,
TestCase,
class ContactSerializerPyTest(
ContactSerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -2,91 +2,28 @@ from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_viewset import (
PersonMetadataInheritedCases,
PersonPermissionsAPIInheritedCases,
PersonViewSetInheritedCases
)
class ViewSetBase:
add_data = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item_diff_org = {
'email': 'ipstrange@unit.test',
}
kwargs_create_item = {
'email': 'ipweird@unit.test',
}
model = Contact
url_kwargs: dict = {}
url_view_kwargs: dict = {}
class PermissionsAPITestCases(
ViewSetBase,
PersonPermissionsAPIInheritedCases,
):
pass
class ContactPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.add_data.update(
super().add_data
)
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class ContactPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
pass
class ViewSetTestCases(
ViewSetBase,
PersonViewSetInheritedCases,
):
pass
add_data: dict = {
'email': 'ipfunny@unit.test',
}
kwargs_create_item: dict = {
'email': 'ipweird@unit.test',
}
kwargs_create_item_diff_org: dict = {
'email': 'ipstrange@unit.test',
}
model = Contact
@ -96,21 +33,19 @@ class ContactViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
super().setUpTestData()
@ -120,50 +55,4 @@ class ContactViewSetTest(
ViewSetTestCases,
TestCase,
):
pass
class MetadataTestCases(
ViewSetBase,
PersonMetadataInheritedCases,
):
pass
class ContactMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_item.update(
super().kwargs_create_item
)
self.kwargs_create_item_diff_org.update(
super().kwargs_create_item_diff_org
)
super().setUpTestData()
class ContactMetadataTest(
MetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,24 @@
import pytest
from access.models.entity import Entity
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Entity
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity import ModelSerializer
yield ModelSerializer

View File

@ -1,78 +0,0 @@
from django.test import TestCase
from access.models.entity_history import Entity, EntityHistory
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
class HistoryTestCases(
HistoryEntriesCommon,
):
field_name = 'model_notes'
history_model = EntityHistory
kwargs_create_obj: dict = {}
kwargs_delete_obj: dict = {}
model = Entity
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.obj = self.model.objects.create(
organization = self.organization,
model_notes = self.field_value_original,
**self.kwargs_create_obj,
)
self.obj_delete = self.model.objects.create(
organization = self.organization,
model_notes = 'another note',
**self.kwargs_delete_obj,
)
self.call_the_banners()
class EntityHistoryInheritedCases(
HistoryTestCases,
):
model = None
"""Entity model to test"""
kwargs_create_obj: dict = None
kwargs_delete_obj: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_obj.update(
super().kwargs_create_obj
)
self.kwargs_delete_obj.update(
super().kwargs_delete_obj
)
super().setUpTestData()
class EntityHistoryTest(
HistoryTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,260 @@
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.models.entity import Entity
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from accounting.models.asset_base import AssetBase
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
User = django.contrib.auth.get_user_model()
class EntityMetadataTestCases(
MetadataAttributesFunctional,
):
add_data: dict = {}
app_namespace = 'v2'
base_model = Entity
"""Base model for this sub model
don't change or override this value
"""
change_data = None
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = None
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
. create an organization that is different to item
2. Create a team
3. create teams with each permission: view, add, change, delete
4. create a user per team
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.different_organization = Organization.objects.create(name='test_different_organization')
self.view_user = User.objects.create_user(username="test_user_view", password="password")
self.item = self.model.objects.create(
organization = organization,
**self.kwargs_create_item
)
self.other_org_item = self.model.objects.create(
organization = self.different_organization,
**self.kwargs_create_item_diff_org
)
self.url_view_kwargs.update({ 'pk': self.item.id })
if self.add_data is not None:
self.add_data.update({
'organization': self.organization.id,
})
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
add_permissions = Permission.objects.get(
codename = 'add_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
add_team = Team.objects.create(
team_name = 'add_team',
organization = organization,
)
add_team.permissions.set([add_permissions])
change_permissions = Permission.objects.get(
codename = 'change_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
change_team = Team.objects.create(
team_name = 'change_team',
organization = organization,
)
change_team.permissions.set([change_permissions])
delete_permissions = Permission.objects.get(
codename = 'delete_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
delete_team = Team.objects.create(
team_name = 'delete_team',
organization = organization,
)
delete_team.permissions.set([delete_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.add_user = User.objects.create_user(username="test_user_add", password="password")
TeamUsers.objects.create(
team = add_team,
user = self.add_user
)
self.change_user = User.objects.create_user(username="test_user_change", password="password")
TeamUsers.objects.create(
team = change_team,
user = self.change_user
)
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
TeamUsers.objects.create(
team = delete_team,
user = self.delete_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = self.different_organization,
)
different_organization_team.permissions.set([
view_permissions,
add_permissions,
change_permissions,
delete_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)
def test_sanity_is_entity_sub_model(self):
"""Sanity Test
This test ensures that the model being tested `self.model` is a
sub-model of `self.base_model`.
This test is required as the same viewset is used for all sub-models
of `AssetBase`
"""
assert issubclass(self.model, self.base_model)
class EntityMetadataInheritedCases(
EntityMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
url_name = '_api_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'model_name': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'model_name': self.model._meta.sub_model_type
}
super().setUpTestData()
class EntityMetadataTest(
EntityMetadataTestCases,
TestCase,
):
url_name = '_api_entity'

View File

@ -0,0 +1,89 @@
import pytest
from api.tests.functional.test_functional_api_permissions import (
APIPermissionsInheritedCases,
)
class EntityPermissionsAPITestCases(
APIPermissionsInheritedCases,
):
add_data: dict = {}
app_namespace = 'v2'
change_data = {}
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
url_kwargs: dict = {}
url_name = '_api_entity'
url_view_kwargs: dict = {}
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class EntityPermissionsAPIInheritedCases(
EntityPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_entity_sub'
@pytest.fixture(scope='class')
def inherited_var_setup(self, request):
request.cls.url_kwargs.update({
'model_name': self.model._meta.sub_model_type
})
request.cls.url_view_kwargs.update({
'model_name': self.model._meta.sub_model_type
})
@pytest.fixture(scope='class', autouse = True)
def class_setup(self, request, django_db_blocker,
model,
var_setup,
prepare,
inherited_var_setup,
diff_org_model,
create_model,
):
pass
class EntityPermissionsAPIPyTest(
EntityPermissionsAPITestCases,
):
pass

View File

@ -1,94 +1,171 @@
import django
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.models.organization import Organization
from access.serializers.entity import (
Entity,
ModelSerializer
)
User = django.contrib.auth.get_user_model()
class SerializerTestCases:
class MockView:
kwargs_create_item: dict = {}
""" Model kwargs to create item"""
_has_import: bool = False
"""User Permission
model = Entity
"""Model to test"""
get_permission_required() sets this to `True` when user has import permission.
"""
create_model_serializer = ModelSerializer
"""Serializer to test"""
_has_purge: bool = False
"""User Permission
valid_data: dict = {}
get_permission_required() sets this to `True` when user has purge permission.
"""
_has_triage: bool = False
"""User Permission
get_permission_required() sets this to `True` when user has triage permission.
"""
class EntitySerializerTestCases:
parameterized_test_data: dict = {
"model_notes": {
'will_create': True,
}
}
valid_data: dict = {
'model_notes': 'model notes field'
}
"""Valid data used by serializer to create object"""
@classmethod
def setUpTestData(self):
"""Setup Test"""
self.organization = Organization.objects.create(name='test_org')
self.kwargs_create_item.update({
'model_notes': 'model notes field'
})
@pytest.fixture( scope = 'class')
def setup_data(self,
request,
model,
django_db_blocker,
organization_one,
):
self.valid_data.update({
'organization': self.organization.pk,
'model_notes': 'model notes field'
})
with django_db_blocker.unblock():
self.item = self.model.objects.create(
organization = self.organization,
**self.kwargs_create_item,
)
request.cls.organization = organization_one
valid_data = {}
for base in reversed(request.cls.__mro__):
if hasattr(base, 'valid_data'):
if base.valid_data is None:
continue
valid_data.update(**base.valid_data)
if len(valid_data) > 0:
request.cls.valid_data = valid_data
if 'organization' not in request.cls.valid_data:
request.cls.valid_data.update({
'organization': request.cls.organization.pk
})
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password")
yield
with django_db_blocker.unblock():
request.cls.view_user.delete()
del request.cls.valid_data
def test_serializer_valid_data(self):
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_data,
):
pass
def test_serializer_valid_data(self, create_serializer):
"""Serializer Validation Check
Ensure that when creating an object with valid data, no validation
error occurs.
"""
serializer = self.create_model_serializer(
view_set = MockView()
serializer = create_serializer(
context = {
'view': view_set,
},
data = self.valid_data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_no_model_notes(self):
def test_serializer_valid_data_missing_field_is_valid(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_will_create,
):
"""Serializer Validation Check
Ensure that if creating and no model_notes is provided no validation
error occurs
Ensure that when creating an object with a user with import permission
and with valid data, no validation error occurs.
"""
data = self.valid_data.copy()
del data['model_notes']
valid_data = self.valid_data.copy()
serializer = self.create_model_serializer(
data = data
del valid_data[param_value]
view_set = MockView()
view_set._has_import = True
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
assert serializer.is_valid(raise_exception = True)
is_valid = serializer.is_valid(raise_exception = False)
assert (
(
not param_will_create
and param_will_create == is_valid
)
or param_will_create == is_valid
)
class EntitySerializerInheritedCases(
SerializerTestCases,
EntitySerializerTestCases,
):
create_model_serializer = None
"""Serializer to test"""
kwargs_create_item: dict = None
""" Model kwargs to create item"""
parameterized_test_data: dict = None
model = None
"""Model to test"""
@ -97,10 +174,40 @@ class EntitySerializerInheritedCases(
"""Valid data used by serializer to create object"""
def test_serializer_valid_data_missing_field_raises_exception(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_exception_key,
):
"""Serializer Validation Check
class EntitySerializerTest(
SerializerTestCases,
TestCase,
Ensure that when creating an object with a user with import permission
and with valid data, no validation error occurs.
"""
valid_data = self.valid_data.copy()
del valid_data[param_value]
view_set = MockView()
with pytest.raises(ValidationError) as err:
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
is_valid = serializer.is_valid(raise_exception = True)
assert err.value.get_codes()[param_value][0] == param_exception_key
class EntitySerializerPyTest(
EntitySerializerTestCases,
):
pass
parameterized_test_data: dict = None

View File

@ -1,37 +1,49 @@
from django.contrib.auth.models import Permission, User
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.models.entity import Entity
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
User = django.contrib.auth.get_user_model()
class ViewSetBase:
add_data: dict = None
add_data: dict = {
'model_notes': 'added model note'
}
app_namespace = 'v2'
base_model = Entity
"""Base model for this sub model
don't change or override this value
"""
change_data = None
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item: dict = {
'model_notes': 'added model note'
}
kwargs_create_item_diff_org: dict = {}
kwargs_create_item_diff_org: dict = {
'model_notes': 'added model note'
}
model = None
url_kwargs: dict = None
url_kwargs: dict = {}
url_view_kwargs: dict = None
url_view_kwargs: dict = {}
url_name = None
@ -53,16 +65,15 @@ class ViewSetBase:
self.different_organization = Organization.objects.create(name='test_different_organization')
self.view_user = User.objects.create_user(username="test_user_view", password="password")
self.item = self.model.objects.create(
organization = organization,
model_notes = 'some notes',
**self.kwargs_create_item
)
self.other_org_item = self.model.objects.create(
organization = self.different_organization,
model_notes = 'some more notes',
**self.kwargs_create_item_diff_org
)
@ -71,7 +82,9 @@ class ViewSetBase:
if self.add_data is not None:
self.add_data.update({'organization': self.organization.id})
self.add_data.update({
'organization': self.organization.id,
})
view_permissions = Permission.objects.get(
@ -144,7 +157,6 @@ class ViewSetBase:
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
self.view_user = User.objects.create_user(username="test_user_view", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
@ -190,98 +202,16 @@ class ViewSetBase:
)
class PermissionsAPITestCases(
ViewSetBase,
APIPermissions,
):
add_data: dict = {}
change_data = {'model_notes': 'device'}
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
@classmethod
def setUpTestData(self):
self.add_data.update({ 'model_note': 'added model note' })
super().setUpTestData()
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
def test_sanity_is_asset_sub_model(self):
"""Sanity Test
This test ensures that the model being tested `self.model` is a
sub-model of `self.base_model`.
This test is required as the same viewset is used for all sub-models
of `Entity`
"""
pass
class EntityPermissionsAPIInheritedCases(
PermissionsAPITestCases,
):
add_data: dict = None
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.url_kwargs = {
'entity_model': self.model._meta.model_name
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
}
super().setUpTestData()
class EntityPermissionsAPITest(
PermissionsAPITestCases,
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
assert issubclass(self.model, self.base_model)
@ -290,17 +220,7 @@ class ViewSetTestCases(
SerializersTestCases,
):
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
model = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
model = Entity
@ -310,22 +230,28 @@ class EntityViewSetInheritedCases(
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
url_name = '_api_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'entity_model': self.model._meta.model_name
'model_name': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
'model_name': self.model._meta.sub_model_type
}
super().setUpTestData()
@ -337,168 +263,4 @@ class EntityViewSetTest(
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
class MetadataTestCases(
ViewSetBase,
MetadataAttributesFunctional,
):
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
model = None
url_kwargs: dict = None
url_view_kwargs: dict = None
url_name = None
class EntityMetadataInheritedCases(
MetadataTestCases,
):
model = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.url_kwargs = {
'entity_model': self.model._meta.model_name
}
self.url_view_kwargs = {
'entity_model': self.model._meta.model_name
}
super().setUpTestData()
class EntityMetadataTest(
MetadataTestCases,
TestCase,
):
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
model = Entity
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_entity'
# def test_method_options_request_detail_data_has_key_urls_back(self):
# """Test HTTP/Options Method
# Ensure the request data returned has key `urls.back`
# """
# client = Client()
# client.force_login(self.view_user)
# response = client.options(
# reverse(
# self.app_namespace + ':' + self.url_name + '-detail',
# kwargs=self.url_view_kwargs
# ),
# content_type='application/json'
# )
# assert 'back' in response.data['urls']
# def test_method_options_request_detail_data_key_urls_back_is_str(self):
# """Test HTTP/Options Method
# Ensure the request data key `urls.back` is str
# """
# client = Client()
# client.force_login(self.view_user)
# response = client.options(
# reverse(
# self.app_namespace + ':' + self.url_name + '-detail',
# kwargs=self.url_view_kwargs
# ),
# content_type='application/json'
# )
# assert type(response.data['urls']['back']) is str
# def test_method_options_request_list_data_has_key_urls_return_url(self):
# """Test HTTP/Options Method
# Ensure the request data returned has key `urls.return_url`
# """
# client = Client()
# client.force_login(self.view_user)
# if self.url_kwargs:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
# else:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list')
# response = client.options( url, content_type='application/json' )
# assert 'return_url' in response.data['urls']
# def test_method_options_request_list_data_key_urls_return_url_is_str(self):
# """Test HTTP/Options Method
# Ensure the request data key `urls.return_url` is str
# """
# client = Client()
# client.force_login(self.view_user)
# if self.url_kwargs:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
# else:
# url = reverse(self.app_namespace + ':' + self.url_name + '-list')
# response = client.options( url, content_type='application/json' )
# assert type(response.data['urls']['return_url']) is str
url_name = '_api_entity'

View File

@ -1,81 +0,0 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.models.entity_notes import Entity, EntityNotes
from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields
class NotesAPITestCases(
ModelNotesNotesAPIFields,
):
entity_model = None
model = EntityNotes
kwargs_model_create: dict = None
# url_view_kwargs: dict = None
view_name: str = '_api_v2_entity_note'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Call parent setup
2. Create a model note
3. add url kwargs
4. make the API request
"""
super().setUpTestData()
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.entity_model.objects.create(
organization = self.organization,
model_notes = 'text',
**self.kwargs_model_create
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_view_kwargs = {
'model_id': self.item.model.pk,
'pk': self.item.pk
}
self.make_request()
class EntityNotesAPIInheritedCases(
NotesAPITestCases,
):
entity_model = None
kwargs_model_create = None
class EntityNotesAPITest(
NotesAPITestCases,
TestCase,
):
entity_model = Entity
kwargs_model_create = {}

View File

@ -1,162 +0,0 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.viewsets.entity_notes import ViewSet
from core.tests.abstract.test_functional_notes_viewset import (
ModelNotesViewSetBase,
ModelNotesMetadata,
ModelNotesPermissionsAPI,
ModelNotesSerializer
)
class ViewSetBase(
ModelNotesViewSetBase
):
viewset = ViewSet
kwargs_create_model_item: dict = {}
kwargs_create_model_item_other_org: dict = {}
url_name = '_api_v2_entity_note'
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.item = self.viewset.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.viewset.model.model.field.related_model.objects.create(
organization = self.organization,
model_notes = 'text',
**self.kwargs_create_model_item
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.other_org_item = self.viewset.model.objects.create(
organization = self.different_organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.viewset.model.model.field.related_model.objects.create(
organization = self.organization,
model_notes = 'text',
**self.kwargs_create_model_item_other_org
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_kwargs = {
'model_id': self.item.model.pk,
}
self.url_view_kwargs = {
'model_id': self.item.model.pk,
'pk': self.item.id
}
class NotesPermissionsAPITestCases(
ViewSetBase,
ModelNotesPermissionsAPI,
):
viewset = None
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a global model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class EntityNotesPermissionsAPIInheritedCases(
NotesPermissionsAPITestCases,
):
viewset = None
class EntityNotesPermissionsAPITest(
NotesPermissionsAPITestCases,
TestCase,
):
viewset = ViewSet
class NotesSerializerTestCases(
ViewSetBase,
ModelNotesSerializer,
):
viewset = None
class EntityNotesSerializerInheritedCases(
NotesSerializerTestCases,
):
viewset = None
class EntityNotesSerializerTest(
NotesSerializerTestCases,
TestCase,
):
viewset = ViewSet
class NotesMetadataTestCases(
ViewSetBase,
ModelNotesMetadata,
):
viewset = None
class EntityNotesMetadataInheritedCases(
NotesMetadataTestCases,
):
viewset = None
class EntityNotesMetadataTest(
NotesMetadataTestCases,
TestCase,
):
viewset = ViewSet

View File

@ -1,32 +0,0 @@
from django.test import TestCase
from access.models.organization_history import Organization, OrganizationHistory
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
class History(
HistoryEntriesCommon,
TestCase,
):
model = Organization
history_model = OrganizationHistory
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.obj = self.model.objects.create(
name = self.field_value_original,
)
self.obj_delete = self.model.objects.create(
name = self.field_value_delete,
)
self.call_the_banners()

View File

@ -1,22 +1,25 @@
import django
import pytest
from django.contrib.auth.models import User
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.serializers.organization import (
Organization,
OrganizationModelSerializer
Tenant,
TenantModelSerializer
)
User = django.contrib.auth.get_user_model()
@pytest.mark.skip( reason = 'to be refactored' )
class OrganizationValidationAPI(
TestCase,
):
model = Organization
model = Tenant
@classmethod
def setUpTestData(self):
@ -45,7 +48,7 @@ class OrganizationValidationAPI(
Ensure that if creating and no name is provided a validation error occurs
"""
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = self.valid_data
)
@ -65,7 +68,7 @@ class OrganizationValidationAPI(
with pytest.raises(ValidationError) as err:
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = data
)
@ -85,7 +88,7 @@ class OrganizationValidationAPI(
del data['manager']
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = data
)

View File

@ -1,15 +1,16 @@
import django
import pytest
import unittest
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
from access.models.organization import Organization
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
@ -17,15 +18,18 @@ from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
User = django.contrib.auth.get_user_model()
@pytest.mark.skip( reason = 'to be refactored' )
class ViewSetBase:
model = Organization
app_namespace = 'v2'
url_name = '_api_v2_organization'
url_name = '_api_tenant'
change_data = {'name': 'device'}
@ -315,4 +319,4 @@ class OrganizationMetadata(
menu_id = 'access'
menu_entry_id = 'organization'
menu_entry_id = 'tenant'

View File

@ -1,116 +0,0 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.viewsets.organization_notes import ViewSet
from core.tests.abstract.test_functional_notes_viewset import (
ModelNotesViewSetBase,
ModelNotesMetadata,
ModelNotesPermissionsAPI,
ModelNotesSerializer
)
class ViewSetBase(
ModelNotesViewSetBase
):
viewset = ViewSet
url_name = '_api_v2_organization_note'
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.item = self.viewset.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.organization,
created_by = self.view_user,
modified_by = self.view_user,
)
self.other_org_item = self.viewset.model.objects.create(
organization = self.different_organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.different_organization,
created_by = self.view_user,
modified_by = self.view_user,
)
self.global_org_item = self.viewset.model.objects.create(
organization = self.global_organization,
content = 'a random comment global_organization',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.global_organization,
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_kwargs = {
'model_id': self.item.model.pk,
}
self.url_view_kwargs = {
'model_id': self.item.model.pk,
'pk': self.item.id
}
class OrganizationModelNotesPermissionsAPI(
ViewSetBase,
ModelNotesPermissionsAPI,
TestCase,
):
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a global model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class OrganizationModelNotesSerializer(
ViewSetBase,
ModelNotesSerializer,
TestCase,
):
pass
class OrganizationModelNotesMetadata(
ViewSetBase,
ModelNotesMetadata,
TestCase,
):
pass

View File

@ -1,53 +0,0 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields
from access.models.organization import Organization
from access.models.organization_notes import OrganizationNotes
class OrganizationNotesAPI(
ModelNotesNotesAPIFields,
TestCase,
):
model = OrganizationNotes
view_name: str = '_api_v2_organization_note'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Call parent setup
2. Create a model note
3. add url kwargs
4. make the API request
"""
super().setUpTestData()
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = Organization.objects.create(
name = 'dev'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_view_kwargs = {
'model_id': self.item.model.pk,
'pk': self.item.pk
}
self.make_request()

View File

@ -0,0 +1,24 @@
import pytest
from access.models.person import Person
@pytest.fixture( scope = 'class')
def model(request):
request.cls.model = Person
yield request.cls.model
del request.cls.model
@pytest.fixture(scope='function')
def create_serializer():
from access.serializers.entity_person import ModelSerializer
yield ModelSerializer

View File

@ -1,65 +0,0 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.functional.entity.test_functional_entity_history import (
EntityHistoryInheritedCases
)
class PersonTestCases(
EntityHistoryInheritedCases,
):
field_name = 'model_notes'
kwargs_create_obj: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
kwargs_delete_obj: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
model = Person
class PersonHistoryInheritedCases(
PersonTestCases,
):
model = None
"""Entity model to test"""
kwargs_create_obj: dict = None
kwargs_delete_obj: dict = None
@classmethod
def setUpTestData(self):
self.kwargs_create_obj.update(
super().kwargs_create_obj
)
self.kwargs_delete_obj.update(
super().kwargs_delete_obj
)
super().setUpTestData()
class PersonHistoryTest(
PersonTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,83 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.functional.entity.test_functional_entity_metadata import (
EntityMetadataInheritedCases
)
from accounting.models.asset_base import AssetBase
class PersonMetadataTestCases(
EntityMetadataInheritedCases,
):
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
model = Person
class PersonMetadataInheritedCases(
PersonMetadataTestCases,
):
model = None
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
# self.url_kwargs = {
# 'model_name': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'model_name': self.model._meta.sub_model_type
# }
super().setUpTestData()
class PersonMetadataTest(
PersonMetadataTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,91 @@
import pytest
from access.tests.functional.entity.test_functional_entity_permission import (
EntityPermissionsAPIInheritedCases
)
class PersonPermissionsAPITestCases(
EntityPermissionsAPIInheritedCases,
):
add_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Strange',
'dob': '2025-04-08',
}
# app_namespace = 'v2'
# change_data = {}
# delete_data = {}
kwargs_create_item: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Weird',
'dob': '2025-04-08',
}
kwargs_create_item_diff_org: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
# url_kwargs: dict = {}
# url_name = '_api_entity'
# url_view_kwargs: dict = {}
class PersonPermissionsAPIInheritedCases(
PersonPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
# url_name = '_api_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'model_name': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'model_name': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class PersonPermissionsAPIPyTest(
PersonPermissionsAPITestCases,
):
pass

Some files were not shown because too many files have changed in this diff Show More