Compare commits

...

1194 Commits

Author SHA1 Message Date
3d2d759d6b build: bump version 1.17.1 -> 1.18.0 2025-07-03 22:55:51 +00:00
Jon
b73de03d27 Merge pull request #851 from nofusscomputing/850-fix-itim-validation-failure 2025-07-04 08:08:00 +09:30
Jon
a9e953812c feat(python): upgrade django 5.1.9 -> 5.1.10
ref: #851
2025-07-04 07:47:21 +09:30
Jon
4344265ed5 fix(itim): Correct config that is in the incorrect format
Users can input config that contains bytecode chars which inturn, makes the config entered a str. convert any config that is a str to a dict, the correct format.

ref: #851 fixes #850
2025-07-04 07:42:05 +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
975a71bdd6 build: bump version 1.15.1 -> 1.16.0 2025-05-04 10:50:45 +00:00
Jon
f9e59835c1 Merge pull request #718 from nofusscomputing/feature-next-release 2025-05-04 20:01:58 +09:30
Jon
6ea66f7379 chore: squash migrations
ref: #718
2025-05-04 19:48:15 +09:30
Jon
fb768d1432 Merge pull request #734 from nofusscomputing/test-ticket-model 2025-05-04 03:30:01 +09:30
Jon
baa61155f7 test(core): Serializer Validation for ticket status change for TicketBase model
ref: #734 #723
2025-05-04 03:04:16 +09:30
Jon
c773fbc3a5 test(core): Prevent Closing / Solving of TicketBase Model if not ready
ref: #734 #723 closes #325
2025-05-04 03:04:09 +09:30
Jon
8c84ff7c52 test(itim): Incomplete Model Unit Tests for RequestTicket
ref: #734 #725
2025-05-03 22:56:09 +09:30
Jon
85d4cc8220 test(itim): Incomplete Model Unit Tests for SLMTicketBase
ref: #734 #727
2025-05-03 22:55:14 +09:30
Jon
70a2d502ef test(core): Incomplete Model Unit Tests for TicketBase
ref: #734 #723
2025-05-03 22:53:52 +09:30
Jon
393c8ce0ce refactor(test): rewrite model unit tests to use PyTest
ref: #734 #729 #735
2025-05-03 22:48:14 +09:30
Jon
92bc4aec43 fix(test): correct typo in attribute parameterized_
ref: #734 #729
2025-05-03 04:51:02 +09:30
Jon
4c46681d39 Merge pull request #733 from nofusscomputing/test-ticket-models 2025-05-01 01:42:47 +09:30
Jon
a898549469 test(itim): RequestTicket Updated, yet incomplete Test Suite for Serializer
ref: #733 #725
2025-05-01 01:23:40 +09:30
Jon
4e75faafb8 test(itim): SLMTicketBase Updated, yet incomplete Test Suite for Serializer
ref: #733 #727
2025-05-01 01:23:18 +09:30
Jon
1718a1cf14 test(core): TicketBase Updated, yet incomplete Test Suite for Serializer
ref: #733 #723
2025-05-01 01:22:42 +09:30
Jon
64757826da refactor(test): Update test parameterization
ref: #733 #730 #729
2025-05-01 01:22:22 +09:30
Jon
927776b9a7 chore(test): update pytest and required components
ref: #733
2025-05-01 00:40:06 +09:30
Jon
4ea5ddf122 test: Correct Test Suite for Serializer for models TicketBase, TicketRequest and TicketSLM
ref: #733 #723 #725 #727
2025-04-29 17:20:07 +09:30
Jon
847a5550e8 test(itim): RequestTicket Initial Test Suite for Serializer
ref: #733 #725
2025-04-29 16:14:40 +09:30
Jon
ea38665dab test(itim): SLMTicket Initial Test Suite for Serializer
ref: #733 #727
2025-04-29 16:13:59 +09:30
Jon
440adc09f4 test(core): TicketBase Initial Test Suite for Serializer
ref: #733 #723
2025-04-29 16:13:12 +09:30
Jon
9fcb1a8a44 test(core): SLMTicket Test Suite for ViewSet
ref: #733 #727
2025-04-29 15:59:45 +09:30
Jon
22808cf6c8 test(core): SLMTicket Test Suite for Metadata
ref: #733 #727
2025-04-29 15:59:37 +09:30
Jon
f978839ccc test(core): Request Test Suite for ViewSet
ref: #733 #725
2025-04-29 15:59:07 +09:30
Jon
54af17d75a test(core): Request Test Suite for Metadata
ref: #733 #725
2025-04-29 15:58:57 +09:30
Jon
c7eef1a97c test(core): TicketBase Test Suite for ViewSet
ref: #733 #723
2025-04-29 15:58:29 +09:30
Jon
59741e4197 test(core): TicketBase Test Suite for Metadata
ref: #733 #723
2025-04-29 15:58:05 +09:30
Jon
2518873e23 test(api): update test cases for SubModelViewSet Base Test Class
base class returns different results to inherited class.

ref: #733
2025-04-29 15:52:11 +09:30
Jon
029ab6bc06 refactor(api): SubModelViewSet must inherit from ModelViewSet as it's an extension
ref: #733 #564
2025-04-26 18:05:43 +09:30
Jon
a583db4b65 test(itim): RequestTicket ViewSet Test Suite
ref: #733 #723
2025-04-26 18:04:46 +09:30
Jon
9f8789f390 test(core): TicketBase ViewSet Test Suite
ref: #733 #723
2025-04-26 18:04:22 +09:30
Jon
c0b27c2886 test(api): Incomplete SubModelViewSet Test Cases
ref: #733 #564
2025-04-26 18:03:31 +09:30
Jon
cdacb19c30 test(api): SubModelViewSet Test Suite Setup
ref: #733 #564
2025-04-26 16:45:22 +09:30
Jon
471a1249c0 Merge pull request #732 from nofusscomputing/new-model-ticket-comment-base 2025-04-26 09:09:43 +09:30
Jon
53abf219a3 test: correct tests from Meta.sub_model_type changes
ref: #732
2025-04-26 08:40:29 +09:30
Jon
51c238734a chore(docs): Update issue template - New Model
ref: #732
2025-04-26 07:56:44 +09:30
Jon
efb0f52e8a chore(docs): correct linting errors
ref: #732
2025-04-26 07:48:02 +09:30
Jon
39dc8ad39f feat(core): Add ViewSet for Ticket Comments
ref: #732 #726
2025-04-26 07:38:55 +09:30
Jon
db47d0e160 feat(project_management): Depreciate Project Task Ticket Endpoint
ref: #732 #564
2025-04-26 07:38:10 +09:30
Jon
da13343b27 feat(itim): Depreciate Problem Ticket Endpoint
ref: #732 #564
2025-04-26 07:37:24 +09:30
Jon
8100030870 feat(itim): Depreciate Incident Ticket Endpoint
ref: #732 #564
2025-04-26 07:37:04 +09:30
Jon
5097db6f94 feat(itim): Depreciate Change Ticket Endpoint
ref: #732 #564
2025-04-26 07:36:01 +09:30
Jon
b729842a1c feat(assistance): Depreciate Ticket Comment
ref: #732 #564 #726
2025-04-26 07:34:55 +09:30
Jon
bc1d5ffc03 feat(assistance): Depreciate Request Ticket Endpoint
ref: #732 #564 #725
2025-04-26 07:33:45 +09:30
Jon
ae1f600147 feat(core): Add routes for Ticket Comments
ref: #732 #726
2025-04-26 07:28:25 +09:30
Jon
edc3f9fd3c feat(core): update ticket serializer to use new comment base url
ref: #732 #723
2025-04-26 07:11:12 +09:30
Jon
8dc922a8bb feat(core): Add permissions import, purge and triage to model TicketCommentSolution
ref: #732 #728
2025-04-26 07:10:18 +09:30
Jon
9461cdaf9d feat(core): Add permissions import, purge and triage to model TicketCommentBase
ref: #732 #726
2025-04-26 07:09:33 +09:30
Jon
a923a21660 feat(core): Filter ticket_comment_model routes to those defined in Meta.sub_model_type
ref: #732 #726
2025-04-26 06:56:31 +09:30
Jon
dcd60a5152 feat(core): Filter ticket_model routes to those defined in Meta.sub_model_type
ref: #732 #723
2025-04-26 06:55:54 +09:30
Jon
50882a4732 feat(access): Filter entity_model routes to thos defined in Meta.sub_model_type
ref: #732 #704
2025-04-26 06:53:58 +09:30
Jon
9cd59e8446 feat(core): Serializer for TicketCommentBase
ref: #732 #726
2025-04-26 06:34:58 +09:30
Jon
9905edc28c feat(core): Serializer for TicketCommentSolution
ref: #732 #728
2025-04-26 06:34:12 +09:30
Jon
98479f39e6 feat(core): Ticket Comment Get URL functions
ref: #732 #726
2025-04-26 06:28:18 +09:30
Jon
bdfd3f42c4 feat(core): Ticket Comment Validation for comment_type
must be derived from `Meta.sub_model_type`

ref: #732 #726
2025-04-26 06:27:36 +09:30
Jon
ca022e0697 fix(core): Ticktet comment can have empty body
i.e. time tracking

ref: #732 #726
2025-04-26 06:26:31 +09:30
Jon
1719b6c417 feat(core): Update choices fields for TicketCommentBase model
ref: #732 #726
2025-04-26 06:25:31 +09:30
Jon
bb0f9b56e6 feat(core): init for model TicketCommentSolution
ref: #732 #728
2025-04-26 06:24:00 +09:30
Jon
107a3aa17f feat(core): Migrations for choice fields for TicketBase model
ref: #732 #723
2025-04-26 06:23:33 +09:30
Jon
1c9be27e1f feat(core): Migrations for model TicketCommentSolution
ref: #732 #728
2025-04-26 06:23:07 +09:30
Jon
dc25d483fc feat(core): Update choice fields for TicketBase model
ref: #732 #723
2025-04-26 06:22:25 +09:30
Jon
33362a28d5 feat(core): New model TicketCommentSolution
ref: #732 #728
2025-04-26 06:21:04 +09:30
Jon
b476bd458e fix(core): If model does not save history, dont attempt to cache before
ref: #732
2025-04-26 06:19:35 +09:30
Jon
c152dd13f1 feat(api): when fetching related_object, default to base_model for SubModelViewSet
ref: #732 #726
2025-04-26 06:13:39 +09:30
Jon
677cc55e04 test: correct serializer imports from recent file renames
ref: #732 #726
2025-04-26 06:08:37 +09:30
Jon
b6146b7d14 feat: Add field Meta.sub_model_type to sub-models
ref: #732 #564
2025-04-26 06:00:34 +09:30
Jon
dbc849d3f1 chore(core): rename viewsets/ticket_comment.py -> viewsets/ticket_comment_depreciated.py
ref: #732 #726
2025-04-26 05:49:19 +09:30
Jon
8d1098ec9e fix(itam): provide return_url as part of software version meta
ref: #718
2025-04-25 17:01:52 +09:30
Jon
63923e8a9b Merge pull request #731 from nofusscomputing/refactor-test-api 2025-04-24 13:22:54 +09:30
Jon
7261f60e3b chore(docs): correct linting error
ref: #731
2025-04-24 13:09:00 +09:30
Jon
fef14b8be8 feat(core): New interim model TicketCommentSolution
ref: #731 #728
2025-04-24 12:44:25 +09:30
Jon
17e4cb4148 docs(development): update testing docs
ref: #731 #730
2025-04-24 12:42:00 +09:30
Jon
99211b7505 test: Fixture for creating model with random data
ref: #731 #730
2025-04-24 12:41:16 +09:30
Jon
c4e81ffbed test(itim): API Field checks for TicketSLMBase
ref: #731 #727
2025-04-24 12:36:45 +09:30
Jon
683b134053 test(itim): API Field checks for TicketRequest
ref: #731 #725
2025-04-24 12:36:09 +09:30
Jon
2a5c86d961 fix(itim): correct ticket_slm serializer
ref: #731 #727
2025-04-23 18:58:13 +09:30
Jon
ca28a67194 fix(itim): correct ticket_request serializer
ref: #731 #725
2025-04-23 18:57:41 +09:30
Jon
d904ce7100 chore: migrate app/.coveragerc to -> pyproject.toml
ref: #731
2025-04-23 18:33:35 +09:30
Jon
daf30de835 chore: correct tests so they function and cleanup correctly
ref: #731
2025-04-23 18:33:10 +09:30
Jon
7c9819efd1 test(core): API fields Tests for TicketBase
ref: #731 #723
2025-04-22 23:57:19 +09:30
Jon
42bea64ff5 test(core): API fields Unit Test Suite
ref: #731 #730
2025-04-22 22:25:23 +09:30
Jon
537f167b9e test(core): Correct model notes test suite
ref: #731
2025-04-22 22:22:58 +09:30
Jon
0a630cf51a test(core): API Permission Test Cases for ticket_base model
ref: #731 #723
2025-04-22 22:21:42 +09:30
Jon
29e07e6cac feat(core): add ticket routes
ref: #731 #723
2025-04-22 22:18:40 +09:30
Jon
0f21f8daf0 test(api): add API Permission Test Cases
ref: #731 #730
2025-04-22 22:17:32 +09:30
Jon
6289fad8ad chore: migrate from pytest.ini -> pyproject.toml
ref: #731 #730
2025-04-22 21:44:05 +09:30
Jon
705b245514 chore: add pytest to pyproject.toml
ref: #731
2025-04-20 01:21:10 +09:30
Jon
9e1a426240 Merge pull request #724 from nofusscomputing/refactor-core-model-ticket 2025-04-19 20:38:03 +09:30
Jon
c80a5b5232 docs(development): initial ticket docs
ref: #724 #564 #723
2025-04-16 21:10:59 +09:30
Jon
eddd2534ea fix(api): SubModelViewSet.related_objects must be the same class as the base model
ref: #724 #723 #725
2025-04-16 21:07:14 +09:30
Jon
9dc1ae2d05 feat(itim): serializer for SLMTicketBase
ref: #724 #727
2025-04-16 21:05:32 +09:30
Jon
4b8cb9633e feat(itim): Serializer for RequestTicket
ref: #724 #725
2025-04-16 21:05:15 +09:30
Jon
8016c4b4c8 feat(itim): migrations for RequestTicket
ref: #724 #725
2025-04-16 21:05:00 +09:30
Jon
3efadfa75f feat(itim): New Model RequestTicket
ref: #724 #725
2025-04-16 21:04:45 +09:30
Jon
66bbace8be feat(itim): migration for SLMTicketBase
ref: #724 #727
2025-04-16 21:03:54 +09:30
Jon
7ff638ed58 feat(itim): New Model SLMTicketBase
ref: #724 #727
2025-04-16 21:02:10 +09:30
Jon
ddebf50e88 feat(core): migrations for TicketCommentBase
ref: #724 #726
2025-04-16 21:01:22 +09:30
Jon
64144ea76f feat(core): New Model TicketCommentBase
ref: #724 #726
2025-04-16 21:01:09 +09:30
Jon
633df58964 feat(core): viewset for TicketBase
ref: #724 #723
2025-04-16 21:00:15 +09:30
Jon
f280f408ee feat(core): serializer for TicketBase
ref: #724 #723
2025-04-16 21:00:01 +09:30
Jon
d1715f4457 feat(core): migrations for TicketBase
ref: #724 #723
2025-04-16 20:59:50 +09:30
Jon
e80647ffd1 feat(core): New Model TicketBase
ref: #724 #723
2025-04-16 20:59:36 +09:30
Jon
1a5f328a24 fix(access): Ensure related model is a sub-model
ref: #724 #723
2025-04-16 20:54:30 +09:30
Jon
4872c6d1c8 refactor(core): rename ticket model filename in preparation for base ticket model
ref: #724 #723
2025-04-16 00:38:15 +09:30
Jon
a695b4563d refactor(access): migrate sub-model viewset logic to common
ref: #724 #723
2025-04-15 20:13:39 +09:30
Jon
b89993f89d Merge pull request #722 from nofusscomputing/model-employee 2025-04-15 17:27:30 +09:30
Jon
8f69e6ec79 test(access): Correct history link test cases
ref: #722 #684
2025-04-15 17:09:38 +09:30
Jon
8984aeebb8 test(project_management): Add test cases for api field render for model fields estimation_project and duration_project
ref: #722 closes #312
2025-04-15 16:47:02 +09:30
Jon
a0d750b1f5 feat(project_management): add estimation field to project api fields
ref: #722 closes #312
2025-04-15 16:44:23 +09:30
Jon
75618de977 refactor(project_management): add duration field to project api fields
ref: #722 #312
2025-04-15 16:41:07 +09:30
Jon
2e1358da83 fix(human_resources): Correct history link generation and add docs
ref: #722 #707 fixes #710
2025-04-15 16:20:12 +09:30
Jon
8ecb1fe01a fix(human_resources): Correct history link generation
ref: #722 #684
2025-04-15 16:17:33 +09:30
Jon
0ff8f8650b refactor(human_resources): Move employee details to its own section
ref: #722 #684
2025-04-15 15:46:06 +09:30
Jon
529a7fa385 chore(human_resources): add employee to feature flag 2025-00005
ref: #722 #684 #92
2025-04-15 15:45:43 +09:30
Jon
6258fb2a33 chore(release_notes): add employee note to release notes
ref: #722 closes #684
2025-04-15 15:26:45 +09:30
Jon
0a2ede62b7 test(human_resources): History Serializer and ViewSet Functional test suites for employee
ref: #722 #684
2025-04-15 15:23:35 +09:30
Jon
559e65f990 test(human_resources): APIv2, History, Model and ViewSet Unit test suites for employee
ref: #722 #684
2025-04-15 15:22:56 +09:30
Jon
e71f7a6942 feat(human_resources): nav menu entries for Employee
ref: #722 #684
2025-04-15 15:22:09 +09:30
Jon
f12a2e37f3 feat(human_resources): Serializer for Employee
ref: #722 #684
2025-04-15 15:21:24 +09:30
Jon
c1614b1182 feat(human_resources): Migration for Employee
ref: #722 #684
2025-04-15 15:21:08 +09:30
Jon
6b4bd4db35 feat(human_resources): New model Employee
ref: #722 #684
2025-04-15 15:20:48 +09:30
Jon
163d7bcd6c feat(devops): add missing api index menu entry for devops
ref: #722
2025-04-15 15:09:34 +09:30
Jon
d131d2499b feat(access): add missing nav menu entries for entities
ref: #722
2025-04-15 15:08:38 +09:30
Jon
4e03c2bf31 feat(human_resources): add module to perms selector
ref: #722
2025-04-15 15:07:50 +09:30
Jon
9e05410f28 Merge pull request #719 from nofusscomputing/test-tenancy-model 2025-04-13 16:50:30 +09:30
Jon
9c88c23bb3 test: Migrate models to use refactored model tests
ref: #719 closes #708
2025-04-13 16:35:48 +09:30
Jon
e4588771e8 test: Consolidate All model tests to remove duplicates and to simplify
ref: #719 #708
2025-04-13 13:48:54 +09:30
Jon
507c42faea chore: Add branch coverage to makefile test
ref: #719 #708
2025-04-12 19:04:11 +09:30
Jon
9302627c6f fix(access): add missing attribute to Tenancy object
ref: #718
2025-04-12 19:01:44 +09:30
04780767c0 build: bump version 1.15.0 -> 1.15.1 2025-04-10 05:12:45 +00:00
Jon
2d8275d2af fix(python): Downgrade django 5.2 -> 5.1.8
5.2 removes support for postgres 13

ref: #717
2025-04-10 14:20:27 +09:30
db25eabfdd build: bump version 1.14.0 -> 1.15.0 2025-04-10 03:21:54 +00:00
Jon
71bfb5b9e4 Merge pull request #702 from nofusscomputing/feature-next-release 2025-04-10 12:24:17 +09:30
Jon
3146417001 chore: update release notes
ref: #702
2025-04-10 12:09:41 +09:30
Jon
32d97932d1 chore: squash migrations
reduces the amount required

ref: #702
2025-04-10 12:05:43 +09:30
Jon
32251d0a08 fix(api): Correct documentation link to use models verbose name
ref: #702
2025-04-09 14:41:10 +09:30
Jon
b694df0330 test(settings): Correct nav menu entry for Ticket Category and Ticket Comment Category
ref: #702 #565 #566
2025-04-09 13:59:05 +09:30
Jon
52ffb58276 feat(settings): Move Ticket Comment Category from settings to ITOps menu
ref: #702 closes #566
2025-04-09 13:38:03 +09:30
Jon
9ebc65f3b3 feat(settings): Move Ticket Category from settings to ITOps menu
ref: #702 closes #565
2025-04-09 13:37:27 +09:30
Jon
bdc4066a17 chore(accounting): add feature Flag 2025-00004 to settings ready for use
ref: #702 #714
2025-04-09 12:35:16 +09:30
Jon
ee018802a9 feat(access): place roles nav behind feature flag 2025-00003
ref: #702 #551
2025-04-09 12:31:47 +09:30
Jon
d4221913d2 feat(access): place directory nav behind feature flag 2025-00002
ref: #702 #704
2025-04-09 12:31:00 +09:30
Jon
ab4ebdab24 feat(accounting): add new module
ref: #702 closes #714
2025-04-09 12:28:54 +09:30
Jon
a18d9c2789 chore: add squash link to pr template
ref: #702
2025-04-09 12:12:16 +09:30
Jon
f2b13e5a0c fix(feature_flag): cater for settings flag overrides
ref: #702
2025-04-09 11:22:05 +09:30
Jon
3763eb8727 Merge pull request #713 from nofusscomputing/tests-entities-person-contact 2025-04-08 23:36:57 +09:30
Jon
b99b38f623 test(access): Ensure Model Contacts inherits from Person Model
ref: #713 closes #703
2025-04-08 22:56:42 +09:30
Jon
7aae162453 test(access): Functional Test Suite for Contact API Metadata, API Permissions and ViewSet
ref: #713 #703
2025-04-08 22:51:00 +09:30
Jon
aad1407647 test(access): Functional Test Suite for Contact serializer
ref: #713 #703
2025-04-08 22:50:34 +09:30
Jon
79efc2258f test(access): Functional Test Suite for Contact history
ref: #713 #703
2025-04-08 22:50:24 +09:30
Jon
8b32faa736 test(access): Correct Entity and person functional Test Suite so sub-model testing works
ref: #713 #703
2025-04-08 22:49:49 +09:30
Jon
3b04b0c9a4 test(access): Correct table_fields test case to cater for dynamic field
ref: #713 #703
2025-04-08 22:49:16 +09:30
Jon
4518449232 test(access): Unit Test for Contact ViewSet
ref: #713 #703
2025-04-08 21:55:22 +09:30
Jon
6d708d0c00 test(access): Unit Test for Contact model
ref: #713 #703
2025-04-08 21:55:10 +09:30
Jon
5bf89e1ca6 test(access): Unit Test for Contact history API field checks
ref: #713 #703
2025-04-08 21:55:04 +09:30
Jon
23213c148c test(access): Unit Test for Contact API field checks
ref: #713 #703
2025-04-08 21:54:57 +09:30
Jon
dd4175a6ba test(access): Unit Test for Person Tenancy Object
ref: #713 #703
2025-04-08 21:53:41 +09:30
Jon
1d9ce9d310 test(access): Correct Entity and person unit Test Suite so sub-model testing works
ref: #713 #703
2025-04-08 21:53:21 +09:30
Jon
813470f084 fix(access): Add missing field directory to contact model
ref: #713 #703
2025-04-08 21:52:07 +09:30
Jon
bb27bcc280 feat(access): Ensure that the same person cant be created more than once
based off of combined fields f_name, l_name and dob (if dob is not empty)

ref: #713 #571
2025-04-08 21:09:57 +09:30
Jon
1b53faf9ce test(access): Entity Function Serializer test cases
ref: #713 #571
2025-04-08 21:07:57 +09:30
Jon
33d20a4c0a test(access): Person Model field test cases
ref: #713 #571
2025-04-08 19:43:01 +09:30
Jon
fe7e2b9d22 test(access): Functional Test for Person ViewSet, Permissions and Metadata
ref: #713 closes #571
2025-04-08 19:10:01 +09:30
Jon
c563c07345 test(access): Functional Test for Person History
ref: #713 #571
2025-04-08 19:07:58 +09:30
Jon
3b72df61a4 test(access): Correct Entity Function Test Suite so sub-model testing works
ref: #713 #571
2025-04-08 19:07:41 +09:30
Jon
c3da9503d2 test(access): Unit Test for Person ViewSet
ref: #713 #571
2025-04-08 17:53:59 +09:30
Jon
c42037cef6 test(access): Unit Test for Person Model
ref: #713 #571
2025-04-08 17:53:40 +09:30
Jon
192f46022c test(access): Unit Test for Person History API fields
ref: #713 #571
2025-04-08 17:53:32 +09:30
Jon
81860cac84 test(access): Unit Test for Person API fields
ref: #713 #571
2025-04-08 17:53:16 +09:30
Jon
b66feadb5a test(access): Unit Test for Person Tenancy Object
ref: #713 #571
2025-04-08 17:52:50 +09:30
Jon
b01a2a9a47 test(access): Correct Entity Test Suite so sub-model testing works
ref: #713 #571
2025-04-08 17:49:27 +09:30
Jon
f7d3cdd463 test(app): exclude any field check that ends in _ptr_id
this field is for linking tables and a django field

ref: #713
2025-04-08 17:31:18 +09:30
Jon
3bb63b2a5b Merge pull request #712 from nofusscomputing/model-roles 2025-04-08 16:16:51 +09:30
Jon
05ec53331a fix(settings): Add Application Settings to Admin page
Allows the webmaster to set application settings

ref: #712 fixes #440
2025-04-08 16:00:54 +09:30
Jon
6aebde7845 test(access): Remove teardown from Function Test cases for Role serializer
ref: #712 #683
2025-04-08 15:22:49 +09:30
Jon
4de24a7d88 docs(user): Roles Initial
ref: #712 closes #683
2025-04-07 14:21:19 +09:30
Jon
322b7a1c41 test(access): Test cases for Role serializer
ref: #712 #683
2025-04-07 14:07:08 +09:30
Jon
825683e162 test(access): Function Test cases for Role SPI Permissions, ViewSet and Metadata
ref: #712 #683
2025-04-06 15:17:19 +09:30
Jon
6bb5a47dd3 test(access): Function Test cases for Role History
ref: #712 #683
2025-04-06 15:16:27 +09:30
Jon
47381c7bf7 test(access): Unit Test case to ensure Role is by organization
ref: #712 #683
2025-04-06 14:51:03 +09:30
Jon
8b08d03d95 test(access): Unit Test case to ensure Role cant be set as global object
ref: #712 #683
2025-04-06 14:46:49 +09:30
Jon
360bf60578 test(access): Unit Test cases for Role ViewSet
ref: #712 #683
2025-04-06 14:41:29 +09:30
Jon
c8c2fcabd2 test(access): Unit Test cases for Role model
ref: #712 #683
2025-04-06 14:41:15 +09:30
Jon
1830b86309 test(access): Unit Test cases for Role History API v2
ref: #712 #683
2025-04-06 14:41:07 +09:30
Jon
9e712d3624 test(access): Unit Test cases for Role API v2
ref: #712 #683
2025-04-06 14:40:58 +09:30
Jon
a2a79be7c1 test(access): Unit Test cases for Role Tenancy Object
ref: #712 #683
2025-04-06 14:40:47 +09:30
Jon
d5a2adc3a9 feat(access): Place Roles Model behind feature flag 2025-00003
ref: #712 #683 #551
2025-04-06 14:13:19 +09:30
Jon
779335458e docs(github): model issue template updated to remove model_tag requirement that is not required
ref: #712
2025-04-06 14:01:15 +09:30
Jon
8bc0b3338c refactor(core): When saving history, ensure field _prefetched_objects_cache is not included
ref: #712 #683
2025-04-06 13:51:08 +09:30
Jon
9d8bcff2a0 feat(access): When querying permissions, select related field content_type
speeds up query and reduces required sql queries

ref: #712 #683
2025-04-06 13:50:24 +09:30
Jon
8a8023f510 feat(core): Model tag for Access/Role
ref: #712 #683
2025-04-06 13:49:20 +09:30
Jon
7289758ed9 feat(access): Model Role notes endpoint
ref: #712 #683
2025-04-06 13:46:21 +09:30
Jon
d7405a500d feat(access): Add navigation entry for roles
ref: #712 #683
2025-04-06 13:45:52 +09:30
Jon
d63c249120 feat(access): Model Role History migrations
ref: #712 #683
2025-04-06 13:45:18 +09:30
Jon
e920a66a47 feat(access): Add model Role History
ref: #712 #683
2025-04-06 13:45:07 +09:30
Jon
cb4a12f2a0 feat(access): Role Notes model viewset
ref: #712 #683
2025-04-06 13:44:26 +09:30
Jon
d8263b36f7 feat(access): Role Notes model serializer
ref: #712 #683
2025-04-06 13:44:11 +09:30
Jon
74482956a4 feat(access): Model Role Notes migrations
ref: #712 #683
2025-04-06 13:43:58 +09:30
Jon
1bc199493c feat(access): Add model Role Notes
ref: #712 #683
2025-04-06 13:43:47 +09:30
Jon
5cccca865e feat(access): Role model viewset
ref: #712 #683
2025-04-06 13:43:20 +09:30
Jon
03d258ca57 feat(access): Role model serializer
ref: #712 #683
2025-04-06 13:40:16 +09:30
Jon
dfea1fdba0 feat(access): Model Role migrations
ref: #712 #683
2025-04-06 13:39:37 +09:30
Jon
edff3eb889 feat(access): Add model Role
ref: #712 #683
2025-04-06 13:38:38 +09:30
Jon
bdd55a4df6 Merge pull request #707 from nofusscomputing/new-model-entity 2025-04-04 15:53:04 +09:30
Jon
ca0bb96808 test: During testing add debug_feature_flags so object behind can be tested
ref: #707
2025-04-04 14:56:22 +09:30
Jon
5b0d9d8d81 feat(python): Upgrade Django 5.1.7 -> 5.2
ref: #707
2025-04-04 13:39:45 +09:30
Jon
dd264920a6 feat(access): Place Entity URLs behind feature flag 2025-00002
ref: #707 #704 closes #706
2025-04-04 13:30:49 +09:30
Jon
050b2d7602 test(access): Notes ViewSet Functional Tests for Entity Model
ref: #707 #706
2025-04-04 13:06:27 +09:30
Jon
ea7c359cc8 test(access): Notes API Field Functional Tests for Entity Model
ref: #707 #706
2025-04-04 12:47:04 +09:30
Jon
b3391c7e3e test(access): Correct functional ViewSet test suite for Entity model
ref: #707 #706
2025-04-04 12:11:08 +09:30
Jon
22e369aa3e test(access): History functional Tests for Entity model
ref: #707 #706
2025-04-04 12:02:55 +09:30
Jon
d0f710d9f8 test(access): PermissionsAPI, ViewSet and Metadata Tests for Entity model
ref: #707 #706
2025-04-04 11:39:06 +09:30
Jon
bd58d1d9cf feat(access): Add detail page layout for contact model
ref: #707 #705
2025-04-04 11:27:53 +09:30
Jon
184820780a feat(access): Add Menu entry for corporate directory
ref: #707 #705
2025-04-04 11:27:24 +09:30
Jon
6941668709 feat(access): Add back_url to Entity metadata
ref: #707 #705
2025-04-04 11:26:55 +09:30
Jon
f2cdd403e3 fix(access): Remove app_namespace from Entity
ref: #707 #706
2025-04-04 11:26:10 +09:30
Jon
cb43c56efb docs(user): Add initial contacts
ref: #707 #705
2025-04-04 11:25:33 +09:30
Jon
9bbfbbba4b feat(core): Add Entity model tag
ref: #707 #706
2025-04-04 07:46:08 +09:30
Jon
296188b202 feat(access): Update Entity field entity_type if it does not match the entity type
ref: #707 #706
2025-04-03 19:56:17 +09:30
Jon
de26f4b4e9 docs(development): Entity
ref: #707 #706
2025-04-03 19:29:41 +09:30
Jon
600a12177f feat(access): All Entity models to use the entity history endpoint
ref: #707 #706
2025-04-03 18:33:14 +09:30
Jon
167f4140ba feat(access): Enable specifying the history model to use for audit history for a model
ref: #707
2025-04-03 18:32:50 +09:30
Jon
2d6c347859 fix(access): add missing tenancy object fields to non-tenancy object models
ref: #707
2025-04-03 18:31:51 +09:30
Jon
2998f48e33 fix(core): Dont attempt to fetch history related objects if no history exists
ref: #707
2025-04-03 18:25:08 +09:30
Jon
03a9582703 feat(access): Enable specifying the kb model to use for linking kb article to a model
ref: #707
2025-04-03 18:11:03 +09:30
Jon
3417e37317 feat(access): All Entity models to use the entity notes endpoint
ref: #707 #706
2025-04-03 18:09:48 +09:30
Jon
aba9f44552 feat(access): Enable specifying the notes basename for a model
ref: #707
2025-04-03 17:44:18 +09:30
Jon
569a256dd5 feat(access): ViewSet for Entity Notes model
ref: #707 #706
2025-04-03 17:23:28 +09:30
Jon
22785a095d feat(access): Serializer for Entity Notes model
ref: #707 #706
2025-04-03 17:22:45 +09:30
Jon
209e67e0b1 feat(access): new model Entity Notes
ref: #707 #706
2025-04-03 17:22:28 +09:30
Jon
b50b0bbd8b test(access): Model test cases for Entity
ref: #707 #706
2025-04-03 17:00:00 +09:30
Jon
bbbf7cb38a test(access): API Rendering test cases for Entity model
ref: #707 #706
2025-04-03 17:00:00 +09:30
Jon
3624885f1e test(api): Ensure that when mocking the request the viewset is instantiated
ref: #707
2025-04-03 17:00:00 +09:30
Jon
1f817109ae test(access): History tests for Entity model
ref: #707 #706
2025-04-03 17:00:00 +09:30
Jon
711c546e69 test(access): ViewSet tests for Entity model
ref: #707 #706
2025-04-03 17:00:00 +09:30
Jon
11e32435a2 test(access): Tenancy object test for Entity model
ref: #707 #706
2025-04-03 17:00:00 +09:30
Jon
8c0b9bf182 feat(access): New model Entity History
ref: #707 #706
2025-04-03 17:00:00 +09:30
Jon
44ec81c3ae feat(access): Add Entity URL routes
ref: #707 #706
2025-04-03 17:00:00 +09:30
Jon
24132a0b1c fix(api): Dont attempt to access kwargs if not exists within common serializer
ref: #707
2025-04-03 17:00:00 +09:30
Jon
b887bb6169 feat(access): new serializer Contact
ref: #707 #703
2025-04-03 16:59:59 +09:30
Jon
93ce31d2cf feat(access): new model Contact
ref: #707 #703
2025-04-03 16:59:59 +09:30
Jon
7f4ff50ceb feat(access): new serializer Person
ref: #707 #571
2025-04-03 16:59:59 +09:30
Jon
534dab2ca6 feat(access): new model Person
ref: #707 #571
2025-04-03 16:59:59 +09:30
Jon
705a775ddd feat(access): new ViewSet for for Entity and sub-entities
ref: #707 #706
2025-04-03 16:59:59 +09:30
Jon
7e79385558 feat(access): new serializer Entity
ref: #707 #706
2025-04-03 16:59:59 +09:30
Jon
798cfbe975 feat(access): new model Entity
ref: #707 #706
2025-04-03 16:59:59 +09:30
Jon
bfefbae686 docs: update roadmap
ref: #707
2025-04-03 13:07:16 +09:30
Jon
8363cd379f chore: update issue template for new model
ref: #702
2025-03-29 15:46:55 +09:30
Jon
9496ae6aaf feat(human_resources): Add navigation menu entry for Human Resources (HR)
ref: #702 #569
2025-03-29 15:24:56 +09:30
Jon
5208340370 feat(human_resources): Add module Human Resources (HR) to API Urls
ref: #702 #569
2025-03-29 15:18:37 +09:30
Jon
9b58e5913f feat(base): Add module Human Resources (HR) to installed apps
ref: #702 #569
2025-03-29 15:18:13 +09:30
Jon
748dbea515 feat: Add module Human Resources (HR)
ref: #702 closes #569
2025-03-29 15:10:58 +09:30
b7880de54d build: bump version 1.13.1 -> 1.14.0 2025-03-29 05:22:02 +00:00
Jon
69b727a06c Merge pull request #690 from nofusscomputing/feature-next-release 2025-03-29 14:29:23 +09:30
Jon
26cdd7495a docs(administration): Add reverse proxy paths for containers
ref: #690 closes #674
2025-03-29 13:57:35 +09:30
Jon
e2182fe37e Merge pull request #696 from nofusscomputing/test-viewset 2025-03-29 13:40:57 +09:30
Jon
e8b30796ab docs(development): update testing docs to reflect change in writing tests
ref: #696 #672
2025-03-29 13:14:38 +09:30
Jon
36f314fc6f test(api): Correct test cases for view_name and view_description
cheking type is N/A due to those attributes being set via a getter

ref: #696 #672
2025-03-29 12:35:45 +09:30
Jon
1d1c76e033 test: Refactor all ViewSet Unit Test cases to use new test cases class
ref: #696 closes #672
2025-03-25 02:22:47 +09:30
Jon
e8bc98c315 test(api): Common ViewSet classes Tests and Test cases for classes that inherit them
migrated old viewset test cases to this test file so all common viewset classes
are tested in the one location.

ref: #696 #672
2025-03-25 02:22:26 +09:30
Jon
853906e9ee Merge pull request #694 from nofusscomputing/new-module-itops 2025-03-23 11:06:46 +09:30
Jon
403b6be252 feat(itops): Add navigation menu
ref: #693 closes #567
2025-03-23 10:41:26 +09:30
Jon
ac78032cca feat: New Module ITOps
ref: #694 #567
2025-03-23 10:40:21 +09:30
Jon
08fd187692 Merge pull request #693 from nofusscomputing/model-git-repository 2025-03-23 08:09:03 +09:30
Jon
8bd90df582 feat(devops): Ensure GitHub Groups can't be nested
ref: #693 #515 closes #249
2025-03-23 07:55:47 +09:30
Jon
85a2779563 feat(devops): Models Git Repository must use organization from git_group as must group if parent set
ref: #693 #515
2025-03-23 07:47:05 +09:30
Jon
9cc5db7869 refactor(devops): remove model unique_together constraint for git group and repository
this field is used for sync only

ref: #693 #515
2025-03-23 07:19:49 +09:30
Jon
e65b2531ed refactor(devops): Field provider_id must not be user editable for git group or repository
this field is used for sync only

ref: #693 #515
2025-03-23 07:19:19 +09:30
Jon
3a9198f63c fix(devops): Correct git_group serializer parameter name
ref: #693 #515
2025-03-22 21:25:51 +09:30
Jon
4d8fc508d4 fix(devops): Correct field path to no be unique for git_repository
ref: #693 #515
2025-03-22 21:25:28 +09:30
Jon
67b0187a58 test(api): correct nav menu setup to use mock request
ref: #693
2025-03-22 21:24:40 +09:30
Jon
12ef8918ba fix(feature_flag): if over_rides not set ensure val set to empty dict
ref: #693
2025-03-22 20:53:17 +09:30
Jon
5f3990e15a feat(devops): Add git provider badge to git_group table fields
ref: #693
2025-03-22 20:45:13 +09:30
Jon
491e0dba64 feat(devops): Add git provider badge to git_repository table fields
ref: #693
2025-03-22 20:45:02 +09:30
Jon
50cb54ab0c feat(devops): Add Git GRoup to navigation
ref: #693
2025-03-22 19:25:13 +09:30
Jon
7638fa39da feat(itam): Add back_url to Software Version ViewSet
ref: #693
2025-03-22 19:24:51 +09:30
Jon
b0df5713b2 feat(itam): Add back_url to Operating System ViewSet
ref: #693
2025-03-22 19:24:27 +09:30
Jon
57c5947c55 feat(devops): Add page_layout to Git Group model
ref: #693 #515
2025-03-22 19:23:49 +09:30
Jon
bfd54c112b feat(devops): Add page_layout to GitLab repository model
ref: #693 #515
2025-03-22 19:23:31 +09:30
Jon
e2ca5b8587 feat(devops): Add page_layout to GitHub repository model
ref: #693 #515
2025-03-22 19:23:20 +09:30
Jon
f406e7bf3b feat(devops): git_repository ViewSet updated to fetch queryset based off of repository provider
ref: #693 #515
2025-03-22 19:22:49 +09:30
Jon
1e127d7180 feat(devops): Add ti git_repository ViewSet return and back urls
ref: #693 #515
2025-03-22 19:21:58 +09:30
Jon
a0dd0384bf fix(devops): git_group serializers must define fields
ref: #693 #515
2025-03-22 19:21:04 +09:30
Jon
60cc64ba19 fix(devops): git_group serializers must return urls
ref: #693 #515
2025-03-22 19:20:42 +09:30
Jon
2e9470be83 fix(devops): Correct git_repository notes urls
ref: #693 #515
2025-03-22 19:19:53 +09:30
Jon
ade836911f fix(devops): Correct git_repository url regex
ref: #693 #515
2025-03-22 19:19:40 +09:30
Jon
84f2e8d8c3 feat(devops): Make fields provider and provider_id unique_together for git_repository model
ref: #693 #515
2025-03-22 19:17:33 +09:30
Jon
64b677eaa9 fix(devops): Correct ViewSerializer for GitLab Repository
ref: #693 #515
2025-03-22 19:15:58 +09:30
Jon
4bd5a890db fix(devops): Correct ViewSerializer for GitHib Repository
ref: #693 #515
2025-03-22 19:15:44 +09:30
Jon
48a7a206d2 feat(devops): Add fields to ALL git_repository serializers
ref: #693 #515
2025-03-22 18:55:03 +09:30
Jon
b837338140 fix(devops): Correct model git_group modified field name part 2
ref: #693 #515
2025-03-22 18:53:56 +09:30
Jon
0ad80a6f9a feat(devops): Add fetching of URL to base git_repository model
ref: #693 #515
2025-03-22 18:53:33 +09:30
Jon
a30cad25bc fix(devops): Correct model git_group modified field name
ref: #693 #515
2025-03-22 18:52:35 +09:30
Jon
668a64bb79 fix(api): Fetching of serializer_class must be dynamic
ref: #693
2025-03-22 18:52:00 +09:30
Jon
9d67624e9d feat(api): Enable fetching of app_namespace from model
ref: #693
2025-03-22 18:51:05 +09:30
Jon
ca2e4e00fa feat(access): Add function get_page_layout
enables dynamic page_layout

ref: #693
2025-03-22 18:50:28 +09:30
Jon
57cd4851a8 chore(feature_flag): [2025-00001] add feature flag as enabled when DEBUG=True
so that development can occur

ref: #693 #515
2025-03-22 14:41:53 +09:30
Jon
e6f576ef1a feat(feature_flag): Provide user with ability to override feature flags
ref: #693 #493 #575
2025-03-22 14:40:41 +09:30
Jon
b34c76afde feat(base): Add middleware feature_flag
ref: #693 #515
2025-03-22 13:51:22 +09:30
Jon
f0171fcfda refactor(api): mv _nav property to function get_nav_items
required for dynamic nav menu creation

ref: #693 #515
2025-03-22 13:50:28 +09:30
Jon
04b5b4dc24 feat(devops): Disable notes for GIT Repository Base Model
ref: #693 #515
2025-03-20 16:48:36 +09:30
Jon
77e42db3c9 feat(devops): Add git_repository model tag migration
ref: #693 #515
2025-03-20 16:48:09 +09:30
Jon
25e0f6d950 feat(devops): Add git_repository as a model that can be linked to a ticket
ref: #693 #515
2025-03-20 16:47:39 +09:30
Jon
e725efb9b7 chore: Add missing devops imports
ref: #693 #515
2025-03-20 15:58:26 +09:30
Jon
f6bf6df31c docs(user): Add Git repository and groups
ref: #693 #515
2025-03-20 15:48:54 +09:30
Jon
2994cfd783 feat(devops): Git Group Notes Migration
ref: #693 #515
2025-03-20 15:23:13 +09:30
Jon
d006da803f feat(devops): Git Group Notes ViewSet
ref: #693 #515
2025-03-20 15:23:02 +09:30
Jon
b1f80cb1b2 feat(devops): Git Group Notes Serializer
ref: #693 #515
2025-03-20 15:22:17 +09:30
Jon
9485d4fce7 feat(devops): Git Group Notes Model
ref: #693 #515
2025-03-20 15:22:10 +09:30
Jon
f5e8dd95db feat(devops): GitHub and GitLab Repository Notes Migrations
ref: #693 #515
2025-03-20 15:15:34 +09:30
Jon
83d937ce7a feat(devops): GitLab Repository Notes Viewset
ref: #693 #515
2025-03-20 15:14:31 +09:30
Jon
ee4ff23618 feat(devops): GitHub Repository Notes Viewset
ref: #693 #515
2025-03-20 15:14:15 +09:30
Jon
2b19f466f2 feat(devops): GitLab Repository Notes Serializer
ref: #693 #515
2025-03-20 15:12:06 +09:30
Jon
8caa8646b4 feat(devops): GitHub Repository Notes Serializer
ref: #693 #515
2025-03-20 15:11:58 +09:30
Jon
dc9d1d283f feat(devops): GitLab Repository Notes Model
ref: #693 #515
2025-03-20 15:11:44 +09:30
Jon
0c82fd2bb1 feat(devops): GitHub Repository Notes Model
ref: #693 #515
2025-03-20 15:11:34 +09:30
Jon
5483c1878d feat(devops): Git Group History Migrations
ref: #693 #515
2025-03-20 14:54:10 +09:30
Jon
bd22604d9d feat(devops): Git Group History
ref: #693 #515
2025-03-20 14:54:02 +09:30
Jon
e8c246a949 feat(devops): GitLab and GitHub Repository History Migrations
ref: #693 #515
2025-03-20 14:51:25 +09:30
Jon
69c631d59b feat(devops): GitLab Repository History
ref: #693 #515
2025-03-20 14:51:04 +09:30
Jon
561e175723 feat(devops): GitHub Repository History
ref: #693 #515
2025-03-20 14:50:48 +09:30
Jon
4c6c27a4bd feat(devops): [2025-00001] Git Group and Repositories URLs
behind FF 2025-00001

ref: #693 #515
2025-03-20 14:31:24 +09:30
Jon
cb95cb506a feat(devops): Git Group and Repositories Migrations
ref: #693 #515
2025-03-20 14:24:49 +09:30
Jon
2b3070c5c2 feat(devops): GIT Group ViewSet
ref: #693 #515
2025-03-20 14:14:07 +09:30
Jon
9229036b72 feat(devops): GIT Group Serializer
ref: #693 #515
2025-03-20 14:13:10 +09:30
Jon
b5b8030c81 feat(devops): GIT Group Model
ref: #693 #515
2025-03-20 14:12:58 +09:30
Jon
8d4aad7745 feat(devops): GIT Repositories Viewset
ref: #693 #515
2025-03-20 14:12:29 +09:30
Jon
c5e33c4e3d feat(devops): GitLab Serializer for git repositories
ref: #693 #515
2025-03-20 14:09:12 +09:30
Jon
23773a8776 feat(devops): GitHub Serializer for git repositories
ref: #693 #515
2025-03-20 14:09:00 +09:30
Jon
ef0b024a12 feat(devops): Base Serializer for git repositories
ref: #693 #515
2025-03-20 14:08:16 +09:30
Jon
3cdf0c7324 feat(devops): GitLab Repository Model
ref: #693 #515
2025-03-20 14:07:38 +09:30
Jon
051995efca feat(devops): GitHub Repository Model
ref: #693 #515
2025-03-20 14:07:32 +09:30
Jon
717bd1e221 feat(devops): Base model for git repositories
ref: #693 #515
2025-03-20 14:07:02 +09:30
Jon
e158be3cb2 chore: update issue template new model
ref: #693
2025-03-20 13:39:59 +09:30
Jon
7a7281ecfc Merge pull request #691 from nofusscomputing/test-ticket-slash-commands 2025-03-19 15:50:44 +09:30
Jon
88d1abaef7 fix(core): Don't create an empty ticket comment if the body is empty when slash commands removed
ref: #691 #681
2025-03-19 15:36:13 +09:30
Jon
5804abc367 docs(core): Update slash command docs
ref: #691 #681
2025-03-19 14:51:37 +09:30
Jon
96f8949be2 feat(core): Enable slash command related ticket to have multiple ticket references
ref: #691 #681
2025-03-19 14:38:30 +09:30
Jon
cce802ea9e test(core): un-mark tests as skipped so that multiple linked items per ticket can be tested
ref: #691 closes #681
2025-03-19 13:55:57 +09:30
Jon
c767a528eb fix(core): when processing slash commands trim each line prior to processing
ref: #691 #681
2025-03-19 13:28:11 +09:30
Jon
895cfb8b22 test(core): correct ticket linked item to prevent duplicate creation
ref: #691 #681
2025-03-19 13:27:48 +09:30
Jon
8dfaec8df7 fix(core): slash command NL char is \r\n not \n, however support both
ref: #691 #681
2025-03-19 12:50:56 +09:30
Jon
69cabda78e fix(core): When processing slash commands trim whitespace on return
ref: #691 #681
2025-03-19 12:50:34 +09:30
Jon
8abced87de fix(core): Ensure linked ticket models are unique
ref: #691 #681
2025-03-19 12:00:05 +09:30
Jon
983311dda5 feat(core): Enable slash command linked model to have multiple models
ref: #691 #681
2025-03-19 12:00:05 +09:30
Jon
2becbeef87 feat(core): process ticket slash commands by line
ref: #691 #681
2025-03-19 12:00:05 +09:30
Jon
9583631473 feat(core): Migrations for new slash commands
ref: #690 #592 #598 #600
2025-03-17 15:59:04 +09:30
Jon
44e1461c6b feat(project_management): Add project_state slash command
ref: #690 closes #592
2025-03-17 15:52:18 +09:30
Jon
105e6509b0 feat(core): Add ticket_comment_category slash command
ref: #690 closes #600
2025-03-17 15:51:30 +09:30
Jon
20d979963a feat(core): Add ticket_category slash command
ref: #690 closes #598
2025-03-17 15:38:55 +09:30
Jon
928dee74b5 feat(itam): when displaying software version, add prefix with software name
ref: #690 #596
2025-03-17 15:26:01 +09:30
Jon
bde2b2e758 feat(itam): Add markdown tag $software_version
ref: #690 closes #596
2025-03-17 15:22:53 +09:30
Jon
54b97b43ed feat(itam): Enable ticket tab on software version page
ref: #690 #596
2025-03-17 15:22:07 +09:30
Jon
405538fa35 fix(itam): Add back url to software_version model
ref: #690
2025-03-17 15:19:59 +09:30
7a9680d988 build: bump version 1.13.0 -> 1.13.1 2025-03-17 04:16:28 +00:00
Jon
d7d85bd01d Merge pull request #688 from nofusscomputing/fix-feature-flag-crash 2025-03-17 13:34:04 +09:30
Jon
becb1eef26 fix(devops): After fetching feature flags dont attempt to access results unless status=200
ref: #688 #687
2025-03-17 13:12:29 +09:30
Jon
bf973d3765 fix(docker): only download feature flags when not a worker
ref: #688 fixes #687
2025-03-17 12:23:35 +09:30
Jon
7912a67ab7 refactor(docker): Use crontabs not cron.d
ref: #688 fixes #687
2025-03-17 12:17:59 +09:30
Jon
6272eef45f fix(devops): Use correct stderr function when using feature_flag management command
ref: #688 #687
2025-03-17 11:44:05 +09:30
Jon
176537d583 fix(devops): Cater for connection timeout when fetching feature flags
ref: #688 #687
2025-03-17 11:43:37 +09:30
Jon
2491ab611b fix: when building feature flag version, use first 8 chars of build hash
ref: #688 #687
2025-03-17 11:42:01 +09:30
3925b96cb0 build: bump version 1.12.0 -> 1.13.0 2025-03-16 02:34:25 +00:00
Jon
8488642651 Merge pull request #660 from nofusscomputing/feature-next-release 2025-03-16 11:51:16 +09:30
Jon
cb96d33f74 Merge pull request #679 from nofusscomputing/devops-feature-flagging 2025-03-16 11:35:01 +09:30
Jon
70978b9fdc chore: squash migrations prior to release
ref: #679 #660
2025-03-16 11:18:02 +09:30
Jon
0452293546 docs(development): Notate usage of dict for table field
ref: #679 nofusscomputing/centurion_erp_ui#95 closes #669
2025-03-16 11:09:15 +09:30
Jon
9c65d7f355 test(devops): Feature Flag History API render checks
ref: #679 #659
2025-03-16 09:14:50 +09:30
Jon
32137334ad test(devops): Feature Flag Serializer checks
ref: #679 #659
2025-03-16 09:14:37 +09:30
Jon
a9fb70fcc7 feat(devops): Add ability for user to turn off feature flagging check-in
ref: #679 #496
2025-03-16 08:39:37 +09:30
Jon
c34903abf2 test(devops): CheckIn Entry created of fetching feature flags
ref: #679 #676 #496
2025-03-16 08:25:51 +09:30
Jon
70c7100b1d test(devops): CheckIn model test cases
ref: #679 closes #677
2025-03-16 06:41:43 +09:30
Jon
8eb1650273 chore: update ticket template new model unit test (tenancy)
ref: #679
2025-03-16 06:39:01 +09:30
Jon
68364906f9 chore(devops): Squash All migrations
as this has not been released to production, squash to single migration

ref: #660 #68 #496
2025-03-15 05:03:36 +09:30
Jon
f99c1e2132 Merge pull request #678 from nofusscomputing/devops-check-in 2025-03-15 04:55:13 +09:30
Jon
179340e1a2 feat(devops): When displaying the feature_flag deployments, limit to last 24-hours
ref: #678 #676 #496
2025-03-15 04:35:45 +09:30
Jon
b4d41970aa feat(devops): During feature flag Checkin derive the version from the last field of the user-agent
ref: #678 #676
2025-03-15 04:10:40 +09:30
Jon
5f62828f28 feat(devops): Add missing column to model Checkin
ref: #678 #677
2025-03-15 04:08:58 +09:30
Jon
46996b7f14 feat(devops): Remove model Checkin permissions from permissions selector
ref: #678 #677
2025-03-15 03:42:53 +09:30
Jon
2929e347c7 docs(user): Add how to enable feature flagging
ref: #678 #676
2025-03-15 03:35:01 +09:30
Jon
b8281a3dd7 fix(devops): Only track checkin if no other error occured
ref: #678 #676
2025-03-15 03:33:47 +09:30
Jon
3888ab737b fix(devops): during feature flag checkin, if no client-id provided, use value not-provided
ref: #678 #676
2025-03-15 02:29:37 +09:30
Jon
acc31ef079 feat(devops): Display the days total unique check-ins for feature flags within software feature flagging tab
ref: #678 #676
2025-03-15 02:24:47 +09:30
Jon
19ce303045 feat(devops): Record to check-in table every time feature flags are obtained
ref: #678 #676
2025-03-15 02:21:56 +09:30
Jon
0dbde5e1d6 feat(devops): Migrations for model CheckIns
ref: #678 #677
2025-03-15 02:18:24 +09:30
Jon
f3a45d6ec2 feat(devops): New model CheckIns
ref: #678 #677
2025-03-15 02:16:54 +09:30
Jon
f24f02df5d Merge pull request #675 from nofusscomputing/temp-add-feature-flag 2025-03-14 23:57:16 +09:30
Jon
3ab5babd31 feat: Generate a deployment unique ID
ref: #675 #575
2025-03-14 23:34:35 +09:30
Jon
14c193d72a feat(devops): Provide user with option to disable downloading feature flags
ref: #675 #575
2025-03-14 23:04:57 +09:30
Jon
b22f4e2ea9 feat(devops): Feature Flagging url.path wrapper
ref: #675 #575
2025-03-14 22:38:43 +09:30
Jon
29553474f2 fix(devops): When init the feature flag clients, look for all args within settings
ref: #675 #575
2025-03-13 20:49:18 +09:30
Jon
19d6b4f7e8 fix(devops): Only add Last-Modified header to response if exists
ref: #675 #575
2025-03-13 19:53:59 +09:30
Jon
f1fb3cd7ff feat(docker): Configure cron to download feature flags every four hours
ref: #675 #575
2025-03-13 19:53:00 +09:30
Jon
f1570a1997 feat(docker): Start and run crond within container
ref: #675 #575
2025-03-13 19:52:24 +09:30
Jon
2f95482150 feat(docker): Download feature flags on container start
ref: #675 #575
2025-03-13 19:52:04 +09:30
Jon
b2e82fd8c9 feat(devops): Feature Flagging DRF Router wrapper
ref: #675 #575
2025-03-13 19:49:31 +09:30
Jon
192efadb08 feat(devops): Feature Flagging middleware
ref: #675 #575
2025-03-13 19:49:11 +09:30
Jon
00d16f841f feat(devops): Feature Flagging management command
ref: #675 #575
2025-03-13 19:49:02 +09:30
Jon
c5107861a1 feat(devops): Add Feature Flagging lib
ref: #675 #575
2025-03-13 19:47:44 +09:30
Jon
1d9580f14b feat(devops): add temp application for feature flag client
ref: #675 #575
2025-03-13 19:40:42 +09:30
Jon
aa77c69ed7 Merge pull request #673 from nofusscomputing/public-endpoint-feature-flagging 2025-03-10 20:25:45 +09:30
Jon
acf9f20ab9 test(devops): public feature flag fields corrections
ref: #673 #663
2025-03-10 20:07:12 +09:30
Jon
6cf6a23964 feat(devops): public feature flag endpoint pagination limited to 20 results
ref: #673 closes #663
2025-03-10 19:37:37 +09:30
Jon
a69e102432 test(devops): public feature flag functional ViewSet checks
ref: #673 #663
2025-03-10 19:36:52 +09:30
Jon
89e2de437f fix(devops): Correct logic for data changed check for public endpoint for feature flagging
ref: #673 #663
2025-03-10 19:19:58 +09:30
Jon
dcebd4b8d6 fix(devops): feature flag public ViewSet serializer name correction and qs cache correction
ref: #673 #663
2025-03-10 18:28:18 +09:30
Jon
2e35b651ef test(devops): feature flag ViewSet checks
ref: #673 #663
2025-03-10 16:04:43 +09:30
Jon
2f06814d99 test(api): Update vieset test cases to cater for mockrequest to contain headers attribute
ref: #673
2025-03-10 16:04:09 +09:30
Jon
faff43510d test(devops): feature flag public endpoint API field, header checks
ref: #673 #663
2025-03-10 15:21:07 +09:30
Jon
42105d4621 fix(devops): feature flag public endpoint field modified name typo
ref: #673 #663
2025-03-10 15:19:57 +09:30
Jon
f61799dbfe docs(devops): feature flag if-modified-since header
ref: #673 #663
2025-03-08 21:19:49 +09:30
Jon
427584c8d9 feat(devops): Add support for if-modified-since header for Feature Flags public endpoint
ref: #673 #663
2025-03-08 21:05:44 +09:30
Jon
a07ee8472e fix(devops): Filter public feature flag endpoint to org and software where software is enabled
ref: #673 #663
2025-03-08 19:36:12 +09:30
Jon
3eb02602d6 fix(devops): Move software field filter for feature flag to the serializer
ref: #673 #663
2025-03-08 19:30:45 +09:30
Jon
7dac29971f feat(api): Add public API feature flag index endpoint
ref: #673 #663
2025-03-08 17:49:07 +09:30
Jon
0c6fb786dd feat(api): Add public API endpoint
ref: #673 #663
2025-03-08 17:45:20 +09:30
Jon
d09e649765 feat(devops): Add feature flag public ViewSet
ref: #673 #663
2025-03-08 16:43:34 +09:30
Jon
8741e0b636 feat(devops): Add feature flag public serializer
ref: #673 #663
2025-03-08 16:42:09 +09:30
Jon
529512be3e feat(api): Add common viewset for public RO list
ref: #663
2025-03-08 16:35:00 +09:30
Jon
d038165146 Merge pull request #671 from nofusscomputing/remove-viewset-serializer-caching 2025-03-07 18:05:38 +09:30
Jon
b63f6b7092 feat: Remove serializer caching from ALL viewsets
ref: #671 closes #668
2025-03-07 17:53:14 +09:30
Jon
7b6fe804a9 feat(devops): Add delete col to software enabled feature flags
ref: #659
2025-03-07 17:38:05 +09:30
Jon
b874fc7298 Merge pull request #670 from nofusscomputing/software-feature-flagging 2025-03-07 17:22:32 +09:30
Jon
4f6debac88 fix(devops): Dont attempt to validate feature flag software or organization if it is absent
ref: #670 #659
2025-03-07 17:07:28 +09:30
Jon
5fdc0b32a6 feat(devops): Prevent deletion of software when it has feature flagging enabled and/or feature flags
ref: #670 closes #659
2025-03-07 16:35:04 +09:30
Jon
e607999a62 test(devops): Ensure that only enabled org and enabled software is possible
ref: #670 #659
2025-03-07 16:24:26 +09:30
Jon
5dd4bddea9 fix(devops): Correct feature flagging validation for enabled software and enabled orgs
ref: #670 #659
2025-03-07 16:23:48 +09:30
Jon
b60aa3be7a feat(devops): limit feature_flag to organizations that's had feature flags enabled
ref: #670 #659
2025-03-07 15:45:49 +09:30
Jon
53956e0772 feat(devops): limit feature_flag to software that's had feature flags enabled
ref: #670 #659
2025-03-07 15:45:25 +09:30
Jon
f4ccd3d164 fix(devops): dont cache serializer for featureflag
ref: #670
2025-03-07 15:44:06 +09:30
Jon
629f5aec8e feat(python): Update Django 5.1.5 -> 5.1.7
ref: #670 https://github.com/nofusscomputing/centurion_erp/security/dependabot/10
2025-03-07 13:28:50 +09:30
Jon
69eb7eb294 test(devops): software_feature_flag_enable ViewSet checks
ref: #670 closes #664
2025-03-07 13:25:15 +09:30
Jon
e317a96e45 test(devops): software_feature_flag_enable Serializer checks
ref: #670 #664
2025-03-07 13:24:36 +09:30
Jon
9720ae527a test(devops): Update feature flag test case setup to enable feature flag for testing software
ref: #662 #659
2025-03-05 04:11:20 +09:30
Jon
33644a25d1 test(devops): Update feature flag test case setup to enable feature flag for testing software
ref: #662 #659
2025-03-05 03:32:43 +09:30
Jon
eb7ff47873 fix(devops): Correct Feature Flag serializer validation to cater for edit
ref: #662 #659
2025-03-05 03:32:11 +09:30
Jon
b542e92bbd fix(devops): Feature Flag field is mandatory
ref: #670 #659
2025-03-05 03:31:10 +09:30
Jon
b515b26203 test(api): Remove serializer cache test cases
caching the serializer prevents drf form editor

ref: #670 #664
2025-03-05 02:43:56 +09:30
Jon
b562e09622 test(devops): software_feature_flag_enable api field checks
ref: #670 #664
2025-03-05 02:43:12 +09:30
Jon
748a1c80ef fix(api): make history url dynamic. only display if history should save
ref: #670
2025-03-05 02:42:51 +09:30
Jon
492bbfb521 test(devops): software_feature_flag_enable viewset checks
ref: #670 #664
2025-03-05 02:29:40 +09:30
Jon
86d5ce2062 test(devops): software_feature_flag_enable model checks
ref: #670 #664
2025-03-05 02:29:28 +09:30
Jon
541b7734e5 test(devops): software_feature_flag_enable tenancy object checks
ref: #670 #664
2025-03-05 02:29:12 +09:30
Jon
9fcea0528a feat(devops): Serializer limiting of software and os disabled for time being
ref: #670 #659
2025-03-05 02:28:05 +09:30
Jon
0a5778258b feat(devops): Serializer validate software and org
ref: #670 #659
2025-03-05 01:34:29 +09:30
Jon
75412df8b6 feat(devops): Serializer software filter to enabled feature_flag software
ref: #670 #659
2025-03-05 01:34:11 +09:30
Jon
28e80bff50 feat(devops): Serializer org filter to enabled feature_flag organizations
ref: #670 #659
2025-03-05 01:33:47 +09:30
Jon
f0ec8e4e56 feat(devops): Add endpoint for enabling software for feature flagging
ref: #670 #664
2025-03-05 01:32:49 +09:30
Jon
8124d58014 feat(devops): Add serializer for enabling software for feature flagging
ref: #670 #664
2025-03-05 01:32:23 +09:30
Jon
9f1a73d7a5 feat(devops): Add model for enabling software for feature flagging
ref: #670 #664
2025-03-05 01:32:05 +09:30
Jon
b411d1fb24 fix(devops): if software is deleted delete feature flags
ref: #659 #670
2025-03-05 01:29:43 +09:30
Jon
41727d0a16 Merge pull request #662 from nofusscomputing/feature-flagging 2025-03-04 06:11:12 +09:30
Jon
2eafb88367 fix(core): disable of notes for models not requiring it
ref: #662 #665
2025-03-04 05:58:26 +09:30
Jon
6858c04bfd fix(api): when generating notes url, use correct object
ref: #662 #659
2025-03-04 04:19:27 +09:30
Jon
124c96512a feat(devops): Add model tag feature_flag to ticket linked item
ref: #662 #659
2025-03-04 03:56:57 +09:30
Jon
23793e2133 fix(api): Add missing import for featurenotused
ref: #662
2025-03-04 03:39:53 +09:30
Jon
4b06d6a2a1 feat(devops): Add KB tab to feature flag model
ref: #662 #659
2025-03-04 03:34:51 +09:30
Jon
8a56fdfcdb feat(devops): Add Notes to feature flag model
ref: #662 #659
2025-03-04 03:34:30 +09:30
Jon
c4b640fb53 test(devops): correct dir name for tests
ref: #662 #659
2025-03-04 03:33:27 +09:30
Jon
1afa102679 test(devops): Notes feature flag model checks
ref: #662 #659
2025-03-04 03:33:09 +09:30
Jon
ddf3449b3f feat(core): Migration for feature_flag model reference
ref: #662 #659
2025-03-04 03:30:59 +09:30
Jon
95c5f271ba feat(core): url endpoints added for ticket comment category and ticket category notes
ref: #662 #665
2025-03-04 03:30:12 +09:30
Jon
db0cf389c3 feat(itam): disable model notes for model device os
ref: #662 #665
2025-03-04 03:29:34 +09:30
Jon
8b17ea54c7 feat(api): disable model notes for model auth token
ref: #662 #665
2025-03-04 03:29:16 +09:30
Jon
330d00a73d feat(core): disable model notes for model teamuser
ref: #662 #665
2025-03-04 03:28:56 +09:30
Jon
5f691748bc feat(core): disable model notes for model notes
ref: #662 #666 #667
2025-03-04 03:24:57 +09:30
Jon
4876919015 feat(core): Migrations for adding notes to ticket category and ticket comment category
ref: #662 #666 #667
2025-03-04 03:24:20 +09:30
Jon
55e30ab4f5 test(core): Ticket Comment Category Notes checks
ref: #662 closes #666
2025-03-04 03:24:08 +09:30
Jon
4d6438833d test(core): Ticket Category Notes checks
ref: #662 closes #667
2025-03-04 03:23:27 +09:30
Jon
5b97f5400f fix(core): Add ability to add notes for ticket comment category
ref: #662 #666
2025-03-04 03:23:04 +09:30
Jon
8a787a516f fix(core): Add ability to add notes for ticket category
ref: #662 #667
2025-03-04 03:22:04 +09:30
Jon
51013d12d3 test(app): Model test cases for api field rendering _urls.notes
ref: #662 closes #665
2025-03-04 03:21:30 +09:30
Jon
44a750f32b test(app): Model test cases for get_url_kwargs_notes function
ref: #662 #665
2025-03-04 03:20:39 +09:30
Jon
5938a51193 test(access): Correct Team notes url route name
ref: #662 #665
2025-03-04 03:18:50 +09:30
Jon
f833121c08 fix(core): Serializer _urls.notes URL generation now dynamic
ref: #662 #665
2025-03-04 03:17:31 +09:30
Jon
90a1e4baad feat(core): Add Feature Flag model reference
ref: #662 #659
2025-03-03 20:22:18 +09:30
Jon
20a1f69077 docs(admin): initial feature flags
ref: #662 #661 #496 closes #659
2025-03-02 02:51:19 +09:30
Jon
d79b13d98e docs(user): initial feature flags
ref: #662 #659 #661 #496
2025-03-02 02:39:37 +09:30
Jon
05cf5b2835 docs(user): Add devops module
ref: #662 closes #656
2025-03-02 02:14:27 +09:30
Jon
1edc398f41 fix(api): Dont attempt to access model.get_app_namespace if it doesnt exist
ref: #656 #662
2025-03-02 01:10:00 +09:30
Jon
adfeec5fef feat(devops): Add devops module to installed applications
ref: #662 #656
2025-03-02 00:48:38 +09:30
Jon
7fbe6fda95 test(devops): Feature Flag viewset unit Checks
ref: #662 #659
2025-03-02 00:37:13 +09:30
Jon
40f7f7739f test(devops): Feature Flag model Checks
ref: #662 #659
2025-03-02 00:37:03 +09:30
Jon
51807b4747 test(devops): Feature Flag api Checks
ref: #662 #659
2025-03-02 00:36:55 +09:30
Jon
ef1742c537 test(devops): Feature Flag tenancy object Checks
ref: #662 #659
2025-03-02 00:36:45 +09:30
Jon
1216092413 test(devops): Feature Flag viewset functional Checks
ref: #662 #659
2025-03-02 00:36:25 +09:30
Jon
ee23cb1f6e test(devops): Feature Flag serializer Checks
ref: #662 #659
2025-03-02 00:36:12 +09:30
Jon
158e8436d8 test(devops): Feature Flag History Checks
ref: #662 #661
2025-03-02 00:35:41 +09:30
Jon
235e4db5b6 feat(devops): Add Feature Flag viewset
ref: #662 #659
2025-03-02 00:34:57 +09:30
Jon
aa1cd3eda2 feat(devops): Add Feature Flag serializer
ref: #662 #659
2025-03-02 00:33:59 +09:30
Jon
1ed96ff9fc feat(devops): Add devops Navigation menu
ref: #662 #656
2025-03-02 00:32:47 +09:30
Jon
6fabdb6d17 feat(devops): Add devops module URL includes
ref: #662 #656
2025-03-02 00:32:03 +09:30
Jon
c6a38684db feat(devops): Add devops to permissions
ref: #662 #656
2025-03-02 00:29:23 +09:30
Jon
4ecc841462 feat(devops): DB Migrations for Feature Flag and History model
ref: #662 #661 #659
2025-03-02 00:28:38 +09:30
Jon
eda1fb673b feat(devops): Add Feature Flag History model
ref: #662 #661
2025-03-02 00:28:04 +09:30
Jon
d23e05ac7b feat(devops): Add Feature Flag model
ref: #662 #659
2025-03-02 00:27:43 +09:30
Jon
1818ee94e7 feat(access): add support for nested application namespaces
ref: #656 #662
2025-03-02 00:22:58 +09:30
Jon
9d88bf8827 feat(devops): Add devops module
ref: 656
2025-03-01 20:50:58 +09:30
fa03f7471b build: bump version 1.11.0 -> 1.12.0 2025-03-01 10:43:29 +00:00
Jon
a28a295d19 Merge pull request #652 from nofusscomputing/feature-next-release 2025-03-01 19:56:32 +09:30
Jon
f9abd0101e chore(api): add missing migration for authtoken.meta
ref: #652
2025-03-01 00:43:54 +09:30
Jon
053839af9a feat(api): Add delete column to AuthToken Table
ref: #652 nofusscomputing/centurion_erp_ui#88 closes #647
2025-02-28 23:52:01 +09:30
Jon
5abb4172a9 Merge pull request #651 from nofusscomputing/user-token-endpoint 2025-02-26 20:56:53 +09:30
Jon
d8cfff12ad build: update gitlab-ci to current head
ref: #651
2025-02-26 03:28:55 +09:30
Jon
526e9e876b docs(development): remove old history model pages and links
ref: #651
2025-02-26 03:05:02 +09:30
Jon
88163a954f feat(docker): Upgrade system packages on build
ref: #650
2025-02-26 02:52:51 +09:30
Jon
63184edc88 feat(api): AuthToken requires viewset get_back_url
ref: #650 #649
2025-02-26 01:59:27 +09:30
Jon
e3ec1a4da3 test(api): AuthToken ViewSet checks (unit)
ref: #650 #649
2025-02-26 01:24:52 +09:30
Jon
77316fc682 test(api): AuthToken API Field checks
ref: #650 #649
2025-02-26 01:01:48 +09:30
Jon
38208f3235 test(api): AuthToken Serializer checks
ref: #650 #649
2025-02-26 00:43:34 +09:30
Jon
04ffe056ae test(api): AuthToken ViewSet checks
ref: #650 #649
2025-02-26 00:06:06 +09:30
Jon
1372609e83 fix(api): correct usage of AuthToken.generate to a property
ref: #650 #649
2025-02-25 23:01:48 +09:30
Jon
c62f472546 feat(api): Add auth token api endpoint
ref: #650 #649
2025-02-25 22:33:54 +09:30
Jon
1b85ffca3e feat(settings): Add section title to auth tokens
ref: #650 #649
2025-02-25 22:33:32 +09:30
Jon
57cb5b4105 feat(settings): Add tokens url to user settings _urls
ref: #650 #649
2025-02-25 22:33:10 +09:30
Jon
8597c5e474 feat(api): Update Auth Token model for use with serializer
ref: #650 #649
2025-02-25 22:32:40 +09:30
Jon
eaabbb06c3 feat(api): Add user Auth Token viewset
ref: #650 #649
2025-02-25 22:31:07 +09:30
Jon
5bc7d585ad feat(api): Add user Auth Token serializer
ref: #650 #649
2025-02-25 22:30:32 +09:30
Jon
6c1690fccf feat(settings): Add page_layout attribute to User Settings
ref: #647 #648
2025-02-24 02:11:24 +09:30
f158bed904 build: bump version 1.10.1 -> 1.11.0 2025-02-21 11:36:36 +00:00
Jon
9503f88155 Merge pull request #570 from nofusscomputing/feature-next-release 2025-02-21 20:48:34 +09:30
Jon
79199e27c7 chore(core): Remove old history permissions from available permissions
ref: #605 #601
2025-02-21 02:31:00 +09:30
Jon
3058f7212a chore(core): Squash history commits
to reduce amount of migrations

ref: #605 closes #601
2025-02-21 02:30:48 +09:30
Jon
57d41d12a3 chore(core): Remove old history tests
ref: #605 #601 closes #46
2025-02-20 22:37:54 +09:30
Jon
fe997ee2f8 feat(core): Enable App settings History to save without specifying an organization
APP settings do not form part of an organization

ref: #605 closes #642
2025-02-20 22:37:17 +09:30
Jon
cecad1c368 fix(settings): App settings serializer fielad name does not exist
ref: #605 #642
2025-02-20 22:34:51 +09:30
Jon
df2120ab85 test(settings): History Entry checks for App Settings History
ref: #605 #642
2025-02-20 22:34:26 +09:30
Jon
63400c9ab6 test(settings): API Field Checks for App Settings History
ref: #605 #642
2025-02-20 22:34:02 +09:30
Jon
4ea29dedb9 feat(settings): save_history method added to App Settings
ref: #605 #642
2025-02-20 22:33:06 +09:30
Jon
701608b3ed feat(settings): History Model for App Settings Version added
ref: #605 #642
2025-02-20 22:32:46 +09:30
Jon
4bf54babb2 Merge pull request #605 from nofusscomputing/refactor-model-history 2025-02-20 06:58:06 +09:30
Jon
c1873e8c6d test: Model History not to save history on self
ref: #605 #602
2025-02-20 06:44:30 +09:30
Jon
31eded4f69 docs: remove api pages no longer required
ref: #605 #602
2025-02-20 06:38:12 +09:30
Jon
94737e7ae7 test(core): Correct lookup for model history test setup
ref: #605 #602
2025-02-20 06:28:50 +09:30
Jon
13d697e7de feat(core): Migration for history data to new history tables
ref: #605 closes #602
2025-02-20 06:07:36 +09:30
Jon
37aa9ff968 test(access): remove test cases for Team prarent_object
not required for model

ref: #605 #608
2025-02-20 05:41:00 +09:30
Jon
1663e98a1a fix(access): dont use organization property within organization model
ref: #605 #607
2025-02-20 05:40:01 +09:30
Jon
f765fe1fa0 test(access): History Entry checks for Team model
ref: #605 closes #608
2025-02-19 23:39:42 +09:30
Jon
1561b3a2e6 test(access): API Field Checks for Team History
ref: #605 #608
2025-02-19 23:39:28 +09:30
Jon
4174bacea1 feat(access): save_history method added to Team
ref: #605 #608
2025-02-19 23:39:00 +09:30
Jon
553f4d8579 feat(access): History Model for Team added
ref: #605 #608
2025-02-19 23:38:44 +09:30
Jon
72314ea2a4 test(access): History Entry checks for Organization model
ref: #605 closes #607
2025-02-19 23:38:17 +09:30
Jon
2c2ed724c3 test(access): API Field Checks for Organization History
ref: #605 #607
2025-02-19 23:37:28 +09:30
Jon
0275c7a211 feat(access): save_history method added to Organization
ref: #605 #607
2025-02-19 23:36:36 +09:30
Jon
b2f1fba86a feat(access): History Model for Organization added
ref: #605 #607
2025-02-19 23:36:22 +09:30
Jon
61207c55f9 Merge pull request #641 from nofusscomputing/refactor-access-models 2025-02-19 22:46:52 +09:30
Jon
cabb8d8c4e refactor(core): Update access imports to new path
ref: #641 closes #640
2025-02-19 22:34:55 +09:30
Jon
ef1f764b4b refactor(core): Update access imports to new path
ref: #640 #641
2025-02-19 22:20:41 +09:30
Jon
19ccc5af9b refactor: Update migrations imports to new path
ref: #640 #641
2025-02-19 22:17:49 +09:30
Jon
90dd1a3ded refactor(config_management): Update access imports to new path
ref: #640 #641
2025-02-19 22:02:34 +09:30
Jon
bc8af15d8b refactor(api): Update access imports to neew path
ref: #640 #641
2025-02-19 22:02:27 +09:30
Jon
3884253a2b refactor(settings): Update access imports to new path
ref: #640 #641
2025-02-19 21:55:05 +09:30
Jon
a9698eada1 refactor(project_management): Update access imports to new path
ref: #640 #641
2025-02-19 21:49:46 +09:30
Jon
e91ba06256 refactor(itim): Update access imports to new path
ref: #640 #641
2025-02-19 21:41:49 +09:30
Jon
c506925f6e refactor(itam): Update access imports to new path
ref: #640 #641
2025-02-19 21:30:37 +09:30
Jon
b007e05386 refactor(core): Update access imports to new path
ref: #640 #641
2025-02-19 21:11:01 +09:30
Jon
cadcb8b5d2 refactor(config_management): Update access imports to new path
ref: #640 #641
2025-02-19 20:55:30 +09:30
Jon
cc6d577978 refactor(assistance): Update access imports to new path
ref: #640 #641
2025-02-19 20:50:40 +09:30
Jon
7f2eaaca6a refactor(base): Update access imports to new path
ref: #640 #641
2025-02-19 20:42:44 +09:30
Jon
9b0d09e001 refactor(api): Update access imports to neew path
ref: #640 #641
2025-02-19 20:39:42 +09:30
Jon
77226f75fc refactor(access): Update access imports to neew path
ref: #640 #641
2025-02-19 20:34:28 +09:30
Jon
aeff3f610c refactor(access): Move models to their own file
ref: #640 #641
2025-02-19 20:33:41 +09:30
Jon
635159f364 test: Fix History API checks for kb
ref: #605 #602
2025-02-18 04:13:40 +09:30
Jon
c0e8769781 test: Fix History API checks for project milestone
ref: #605 #602
2025-02-18 04:13:40 +09:30
Jon
04491fbc34 test: Fix History Entry checks for models
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
89e6bc362c fix(project_management): Project milestone is not a child model
ref: #605 #631
2025-02-18 04:13:39 +09:30
Jon
79511d0270 test(config_management): History Entry checks for Config_group_hosts model
ref: #605 closes #637
2025-02-18 04:13:39 +09:30
Jon
69bb6f149f test(settings): History Entry checks for External Link model
ref: #605 closes #635
2025-02-18 04:13:39 +09:30
Jon
381ceb723d test(project_management): History Entry checks for Project Type model
ref: #605 closes #634
2025-02-18 04:13:39 +09:30
Jon
67e010e001 test(project_management): History Entry checks for Project State model
ref: #605 closes #632
2025-02-18 04:13:39 +09:30
Jon
1a1317b584 test(project_management): History Entry checks for Project Milestone model
ref: #605 closes #631
2025-02-18 04:13:39 +09:30
Jon
2a3051c2ae test(project_management): History Entry checks for Project model
ref: #605 closes #630
2025-02-18 04:13:39 +09:30
Jon
dcc86a824a test(itim): History Entry checks for Service model
ref: #605 closes #629
2025-02-18 04:13:39 +09:30
Jon
44a6d1da2d test(itim): History Entry checks for Cluster Type model
ref: #605 closes #627
2025-02-18 04:13:39 +09:30
Jon
bd06be083b test(itim): History Entry checks for Port model
ref: #605 closes #628
2025-02-18 04:13:39 +09:30
Jon
2a14cd6119 test(itim): History Entry checks for Cluster model
ref: #605 closes #626
2025-02-18 04:13:39 +09:30
Jon
334dbfdf7f test(itam): History Entry checks for Software Version model
ref: #605 closes #625
2025-02-18 04:13:39 +09:30
Jon
0c961d2709 test(itam): History Entry checks for Software Category model
ref: #605 closes #624
2025-02-18 04:13:39 +09:30
Jon
99900a2e90 test(itam): History Entry checks for Software model
ref: #605 closes #623
2025-02-18 04:13:39 +09:30
Jon
ede4009933 test(itam): History Entry checks for Operating System Version model
ref: #605 closes #622
2025-02-18 04:13:39 +09:30
Jon
1323531254 test(itam): History Entry checks for Operating System model
ref: #605 closes #621
2025-02-18 04:13:39 +09:30
Jon
e9022edb1d test(itam): History Entry checks for Device Type model
ref: #605 closes #620
2025-02-18 04:13:39 +09:30
Jon
749fe57d4f test(itam): History Entry checks for Device OS model
ref: #605 closes #618
2025-02-18 04:13:39 +09:30
Jon
5e9f2a4763 test(itam): History Entry checks for Device Model model
ref: #605 closes #617
2025-02-18 04:13:39 +09:30
Jon
edb5148b7d test(itam): History Entry checks for Device model
ref: #605 closes #616
2025-02-18 04:13:39 +09:30
Jon
3d98f54b3c test(core): History Entry checks for Ticket Comment Category model
ref: #605 closes #615
2025-02-18 04:13:39 +09:30
Jon
126516a1ff test(core): History Entry checks forTicket Category model
ref: #605 closes #614
2025-02-18 04:13:39 +09:30
Jon
7169bb1618 test(config_management): History Entry checks for Config Groups Software model
ref: #605 closes #612
2025-02-18 04:13:39 +09:30
Jon
9cd72e604e test(config_management): History Entry checks for Config Groups model
ref: #605 closes #611
2025-02-18 04:13:39 +09:30
Jon
135fba1f50 test(assistance): History Entry checks for Knowledge base category model
ref: #605 closes #610
2025-02-18 04:13:39 +09:30
Jon
80fa12b747 test(assistance): History Entry checks for Knowledge base model
ref: #605 closes #609
2025-02-18 04:13:39 +09:30
Jon
77786d28e7 test(itam): Device Software History Entry checks
ref: #605 closes #619
2025-02-18 04:13:39 +09:30
Jon
2775e0a01b test(core): Manufacturer History Entry checks
ref: #605 closes #613
2025-02-18 04:13:39 +09:30
Jon
dd312c6351 fix(core): Child models on delete must make model field null
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
91c964b581 fix(project_management): Project Milestone History is a primaryu model
ref: #605 #631
2025-02-18 04:13:39 +09:30
Jon
db5ce2352e test(core): Model History Entries Test Suite
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
5c3c26c318 fix(core): When a child model is deleted ensure entry is still created on parent model history
ref: #605 #601
2025-02-18 04:13:39 +09:30
Jon
f77d778cb7 test(core): History Model Unit test cases for model and tenancy checks
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
9731cb0eaa test(settings): API Field Checks for External Links History
ref: #605 #635
2025-02-18 04:13:38 +09:30
Jon
e31bc90869 test(project_management): API Field Checks for Project Type History
ref: #605 #634
2025-02-18 04:13:38 +09:30
Jon
a5156bd476 test(project_management): API Field Checks for Project State History
ref: #605 #632
2025-02-18 04:13:38 +09:30
Jon
675f46ce66 test(project_management): API Field Checks for Project Milestone History
ref: #605 #631
2025-02-18 04:13:38 +09:30
Jon
ccb85b16ae test(project_management): API Field Checks for Project History
ref: #605 #630
2025-02-18 04:13:38 +09:30
Jon
9d7750ce44 test(itim): API Field Checks for Service History
ref: #605 #629
2025-02-18 04:13:38 +09:30
Jon
d10faa4b3c test(itim): API Field Checks for Port History
ref: #605 #628
2025-02-18 04:13:38 +09:30
Jon
7028cbf02b test(itim): API Field Checks for Cluster Type History
ref: #605 #627
2025-02-18 04:13:38 +09:30
Jon
3a2d66a213 test(itim): API Field Checks for Cluster History
ref: #605 #626
2025-02-18 04:13:38 +09:30
Jon
9f03b52f58 test(core): API Field Checks for Ticket Comment Category History
ref: #605 #615
2025-02-18 04:13:38 +09:30
Jon
38ff4b9ee7 test(core): API Field Checks for Ticket Category History
ref: #605 #614
2025-02-18 04:13:38 +09:30
Jon
a70a2019b4 test(config_management): API Field Checks for Config Group Software History
ref: #605 #612
2025-02-18 04:13:38 +09:30
Jon
edef6aa2bc test(config_management): API Field Checks for Config Group Hosts History
ref: #605 #637
2025-02-18 04:13:38 +09:30
Jon
4d02276f55 test(config_management): API Field Checks for Config Group History
ref: #605 #611
2025-02-18 04:13:38 +09:30
Jon
2a2ce8e2e0 test(assistance): API Field Checks for Knowledge base category History
ref: #605 #610
2025-02-18 04:13:38 +09:30
Jon
5b0f1b9b82 test(assistance): API Field Checks for Knowledge base History
ref: #605 #609
2025-02-18 04:13:38 +09:30
Jon
58cca3e954 test(itam): API Field Checks for Software Version History
ref: #605 #625
2025-02-18 04:13:38 +09:30
Jon
1ddfd852d4 test(itam): API Field Checks for Software Category History
ref: #605 #624
2025-02-18 04:13:38 +09:30
Jon
22eec00f40 test(itam): API Field Checks for Software History
ref: #605 #623
2025-02-18 04:13:38 +09:30
Jon
23fe7e189e test(itam): API Field Checks for Operating System Version History
ref: #605 #622
2025-02-18 04:13:38 +09:30
Jon
f4edbe80d5 test(itam): API Field Checks for Operating System History
ref: #605 #621
2025-02-18 04:13:38 +09:30
Jon
076b35dedc test(itam): API Field Checks for Device Type History
ref: #605 #620
2025-02-18 04:13:38 +09:30
Jon
4432591168 test(itam): API Field Checks for Device OS History
ref: #605 #618
2025-02-18 04:13:38 +09:30
Jon
bb8a3ba716 test(itam): API Field Checks for Device Model History
ref: #605 #617
2025-02-18 04:13:38 +09:30
Jon
af71907264 test(itam): API Field Checks for Device History
ref: #605 #616
2025-02-18 04:13:38 +09:30
Jon
53d2da43ef test(core): Unit Test Suite for History Model API field checks urls can either be str or hyperlink
ref: #605 #602 #46
2025-02-18 04:13:38 +09:30
Jon
c226a0a910 test(itam): API Field Checks for Device Software History
ref: #605 #619
2025-02-18 04:13:38 +09:30
Jon
4d70925a3d test(core): API Field Checks for Manufacturer History
ref: #605 #613
2025-02-18 04:13:38 +09:30
Jon
6c61ff0121 test(core): API Field Checks for Model History
ref: #605 #601 #602 #46
2025-02-18 04:13:38 +09:30
Jon
48966b9009 test(core): Unit Test Suite for History Model API field checks
ref: #605 #602 #46
2025-02-18 04:13:38 +09:30
Jon
9a428c4e98 feat(core): add org field History Model api rendering
ref: #605 #602  #455
2025-02-18 04:13:38 +09:30
Jon
1ef90e96e0 test(core): Functional Test for History Model APIPermission updated to cater for tenancy obj
ref: #605 #602 closes #455
2025-02-18 04:13:37 +09:30
Jon
74f652aaf4 test(core): Functional Test for History Model API Permissions and Metadata
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
c5999c2e04 fix(core): when fetching url_kwargs for model history, make it dynamic for related field name
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
46e7f3fada fix(core): Xorrect logic for determining view_action
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
afc1c6b6fc test(core): Unit Test for History Model Viewset
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
1b5044cc42 feat(core): Show the model name within history
ref: #605 #601
2025-02-18 04:13:37 +09:30
Jon
413ddbc59b fix(core): dynamically search for history object name
ref: #605 #601
2025-02-18 04:13:37 +09:30
Jon
7e64e23d32 feat(project_management): Project Milestone added to modelhistory.child_history_models
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
3aacc17f54 feat(settings): History Model migrations for External Link
ref: #605 #635
2025-02-18 04:13:37 +09:30
Jon
1485183aab feat(settings): save_history method added to External Link
ref: #605 #635
2025-02-18 04:13:37 +09:30
Jon
14e81e0bdb feat(settings): History Model for External Link added
ref: #605 #635
2025-02-18 04:13:37 +09:30
Jon
a66c441f5a feat(project_management): History Model migrations for Project Type
ref: #605 #634
2025-02-18 04:13:37 +09:30
Jon
1cf844d22d feat(project_management): save_history method added to Project Type
ref: #605 #634
2025-02-18 04:13:37 +09:30
Jon
aaba1c5ad6 feat(project_management): History Model for Project TYpe added
ref: #605 #634
2025-02-18 04:13:37 +09:30
Jon
2b1f8a6e05 feat(project_management): History Model migrations for Project State
ref: #605 #632
2025-02-18 04:13:37 +09:30
Jon
6cd5698f6e feat(project_management): save_history method added to Project State
ref: #605 #632
2025-02-18 04:13:37 +09:30
Jon
eb137c7ae3 feat(project_management): History Model for Project State added
ref: #605 #632
2025-02-18 04:13:37 +09:30
Jon
9ca5cdb88a feat(project_management): History Model migrations for Project Milestone
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
0ec202d633 feat(project_management): save_history method added to Project Milestonr
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
18ba0704ec feat(project_management): History Model for Project Milestone added
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
70d24b1094 feat(project_management): History Model migrations for Project
ref: #605 #630
2025-02-18 04:13:37 +09:30
Jon
a9419fd742 feat(project_management): save_history method added to Project
ref: #605 #630
2025-02-18 04:13:37 +09:30
Jon
e17a0ac5e8 feat(project_management): History Model for Project added
ref: #605 #630
2025-02-18 04:13:37 +09:30
Jon
09bff87030 feat(itim): History Model migrations for Service
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
1b1f9840e3 feat(itim): save_history method added to Service
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
8802d396c5 feat(itim): History Model for Service added
ref: #605 #629
2025-02-18 04:13:37 +09:30
Jon
87f73ac5b0 feat(itim): History Model migrations for Port
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
36de9817b5 feat(itim): save_history method added to Port
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
a3a33085c8 feat(itim): History Model for Port added
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
1c601f5ce1 feat(itim): History Model migrations for Cluster Type
ref: #605 #627
2025-02-18 04:13:37 +09:30
Jon
6b10795b2d feat(itim): save_history method added to Cluster TYpe
ref: #605 #627
2025-02-18 04:13:36 +09:30
Jon
cdf3068b50 feat(itim): History Model for Cluster Type added
ref: #605 #627
2025-02-18 04:13:36 +09:30
Jon
73d5ac6b10 feat(itim): History Model migrations for Cluster
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
18cbd71d28 feat(itim): save_history method added to Cluster
ref: #605 #626
2025-02-18 04:13:36 +09:30
Jon
cf52db1434 feat(itim): History Model for Cluster added
ref: #605 #626
2025-02-18 04:13:36 +09:30
Jon
403b5226cb test(itam): remove test cases for os version model.parent_object as it's not required
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
fcdb2fe057 feat(itam): History Model migrations for Software Version
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
e04e3e4572 feat(itam): save_history method added to Software Version
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
4c14e6db3f feat(itam): History Model for Software Version added
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
e254ec6e78 feat(itam): History Model migrations for Software Category
ref: #605 #624
2025-02-18 04:13:36 +09:30
Jon
f815df41c4 feat(itam): save_history method added to Software Category
ref: #605 #624
2025-02-18 04:13:36 +09:30
Jon
13425c432b feat(itam): History Model for Software Category added
ref: #605 #624
2025-02-18 04:13:36 +09:30
Jon
d273c35baa feat(itam): History Model migrations for Software
ref: #605 #623
2025-02-18 04:13:36 +09:30
Jon
c587ae7102 feat(itam): save_history method added to Software
ref: #605 #623
2025-02-18 04:13:36 +09:30
Jon
cafaf3178a feat(itam): History Model for Software added
ref: #605 #623
2025-02-18 04:13:36 +09:30
Jon
947a9c88fd feat(itam): History Model migrations for Operating System Version
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
ec06c417e1 feat(itam): save_history method added to Operating System Version
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
f954b2507c feat(itam): History Model for Operating System Version added
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
1173029981 feat(itam): History Model migrations for Device Type
ref: #605 #620
2025-02-18 04:13:36 +09:30
Jon
fa0a81e835 feat(itam): save_history method added to Device Type
ref: #605 #620
2025-02-18 04:13:36 +09:30
Jon
e8e3939de7 feat(itam): History Model for Device Type added
ref: #605 #620
2025-02-18 04:13:36 +09:30
Jon
2d7e621251 feat(itam): History Model migrations for Device Operating System
ref: #605 #618
2025-02-18 04:13:36 +09:30
Jon
9132a8f62c feat(itam): save_history method added to Device Operating System
ref: #605 #618
2025-02-18 04:13:36 +09:30
Jon
cda3730943 feat(itam): History Model for Device Operating System added
ref: #605 #618
2025-02-18 04:13:36 +09:30
Jon
70db017ac2 feat(itam): History Model migrations for Operating System
ref: #605 #621
2025-02-18 04:13:36 +09:30
Jon
8055bd79dd feat(itam): save_history method added to Operating System
ref: #605 #621
2025-02-18 04:13:35 +09:30
Jon
a73f5feccc feat(itam): History Model migrations for Operating System
ref: #605 #621
2025-02-18 04:13:35 +09:30
Jon
08a3042866 feat(itam): History Model migrations for Device Software
ref: #605 #619
2025-02-18 04:13:35 +09:30
Jon
86116f4563 feat(itam): History Model for Device Software added
ref: #605 #619
2025-02-18 04:13:35 +09:30
Jon
92fd22ae5c feat(itam): save_history method added to Device
ref: #605 #616
2025-02-18 04:13:35 +09:30
Jon
6ccb63c921 feat(itam): History Model migrations for Device Model
ref: #605 #617
2025-02-18 04:13:35 +09:30
Jon
4e5d2378d8 feat(itam): save_history method added to Device Model
ref: #605 #617
2025-02-18 04:13:35 +09:30
Jon
290a7de736 feat(itam): History Model for Device Model added
ref: #605 #617
2025-02-18 04:13:35 +09:30
Jon
8673862cd3 fix(config_management): Remove parent property from config groups
ref: #605 #611
2025-02-18 04:13:35 +09:30
Jon
1de5c634d0 feat(core): History Model migrations for Ticket Comment Category
ref: #605 #615
2025-02-18 04:13:35 +09:30
Jon
f5d59c703d feat(core): save_history method added to Ticket Comment Category
ref: #605 #615
2025-02-18 04:13:35 +09:30
Jon
0ee8369e24 feat(core): History Model for Ticket Comment Category added
ref: #605 #615
2025-02-18 04:13:35 +09:30
Jon
a67c3fb919 feat(config_management): Child History Models added to child model lists for config group hosts and software
ref: #605 #611 #637
2025-02-18 04:13:35 +09:30
Jon
4dd19ba22f fix(tests): Correct Permission Import due to removing from access.models
ref: #605 #601
2025-02-18 04:13:35 +09:30
Jon
d55ecabb2d feat(core): History Model migrations for Ticket Category
ref: #605 #614
2025-02-18 04:13:35 +09:30
Jon
a0ad877830 feat(core): save_history method added to Ticket Category
ref: #605 #614
2025-02-18 04:13:35 +09:30
Jon
9f192ef28d feat(core): History Model for Ticket Category added
ref: #605 #614
2025-02-18 04:13:35 +09:30
Jon
edf0fca794 feat(core): History Model migrations for Manufacturer
ref: #605 #613
2025-02-18 04:13:35 +09:30
Jon
3f4c8e93fc feat(core): save_history method added to Manufacturer
ref: #605 #613
2025-02-18 04:13:34 +09:30
Jon
1c7b63c4d0 feat(core): History Model for Manufacturer added
ref: #605 #613
2025-02-18 04:13:34 +09:30
Jon
aa43c2e46a feat(config_management): save_history method added to Config Group Software
ref: #605 #637
2025-02-18 04:13:34 +09:30
Jon
4d9e06c55b feat(config_management): save_history method added to Config Group Hosts
ref: #605 #637
2025-02-18 04:13:34 +09:30
Jon
2027c4fd0e feat(config_management): save_history method added to Config Groups
ref: #605 #611
2025-02-18 04:13:34 +09:30
Jon
d708f85883 feat(assistance): save_history method added to Knowledge base
ref: #605 #610
2025-02-18 04:13:34 +09:30
Jon
1d6ec4023a feat(assistance): save_history method added to Knowledge base category
ref: #605 #610
2025-02-18 04:13:34 +09:30
Jon
2059d014d7 feat(config_management): History Model migrations for Config Groupse + children
ref: #605 #611 #612 #637
2025-02-18 04:13:34 +09:30
Jon
9493543b92 feat(config_management): History Model for Config Group Software added
ref: #605 #612
2025-02-18 04:13:34 +09:30
Jon
589c7fab26 feat(config_management): History Model for Config Group Hosts added
ref: #605 #637
2025-02-18 04:13:34 +09:30
Jon
740d33ece1 feat(config_management): History Model for Config Groups added
ref: #605 #611
2025-02-18 04:13:34 +09:30
Jon
0a364f0f7f feat(assistance): History Model migrations for Knowledge base + children
ref: #605 #609 #610
2025-02-18 04:13:34 +09:30
Jon
f8a9e7dcad feat(assistance): History Model for Knowledge base category added
ref: #605 #610
2025-02-18 04:13:34 +09:30
Jon
b77bb3d10c feat(assistance): History Model for Knowledge base added
ref: #605 #609
2025-02-18 04:13:34 +09:30
Jon
d21fc512be test(core): disable hisotry viewset function test
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
6852aa4748 feat(itam): Add device history model
ref: #605 #616
2025-02-18 04:13:34 +09:30
Jon
723bc7aed4 test(core): correct kwargs for history tests
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
358c1720d5 fix(project_management): project Model serializer must inherit common serializer
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
9b8f644261 chore(itam): device operating system+software get_url attribute "obj" renamed to match others "item"
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
57060975b0 chore: remove old history model access from original UI and API V1
ref: #601 #605 closes #606
2025-02-18 04:13:34 +09:30
Jon
24e4c95928 fix(core): History audit objects must be a valid dict
ref: #601 #605 fixes #490
2025-02-18 04:13:34 +09:30
Jon
8f15b8bf20 chore(itam): device operating system get_url attribute "obj" renamed to match others "item"
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
0d6bdad29b fix(api): history app names can contain an underscore
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
4b2be4855b docs(development): History model update
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
b738c4be20 feat(core): History view to only display objects from the model being requested
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
8512bff4f1 fix(core): when saving history, use audit_model for content type
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
507db1051c test(core): Remove old history model viewset tests
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
7cd4cb965b refactor(core): move get_url to common serializer
reduces duplicated code

ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
460bf40175 refactor(api): Update history url kwargs to use vals from model._meta
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
93e7a9097f docs(core): correct history_save code docs
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
1541a89937 fix(core): add missing functions for fetching item url
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
25373fc5b6 test: Disable Old History Model test suites
ref: #601 #602 #605
2025-02-18 04:13:33 +09:30
Jon
a9a5c189d2 feat(core): Add new history model to History Serializer
ref: #601 #602 #605
2025-02-18 04:13:33 +09:30
Jon
f2a995d277 feat(core): Add new history model
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
75b5f48f9e docs(development): Add model history
ref: #601 #605
2025-02-18 04:13:33 +09:30
Jon
bb021a00d9 feat(development): lint for un-used imports
ref: #570
2025-02-18 04:12:59 +09:30
Jon
db07262623 feat(development): add pylit settings
ref: #570
2025-02-18 04:12:59 +09:30
Jon
6f94c95221 feat(core): added new history model
ref: #570 #602
2025-02-18 04:12:59 +09:30
Jon
103f25be1d fix(project_management): Opened by field set to read only for project task ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
cc02976c3a fix(itim): Opened by field set to read only for problem ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
af4a1d52c5 fix(itim): Opened by field set to read only for incident ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
7ac56a35ae fix(itim): Opened by field set to read only for change ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
030560d697 fix(assistance): Opened by field set to read only for request ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
b7dd2dfaba refactor(core): superuser changed from import to triage access
ref: #570
2025-02-18 04:12:59 +09:30
Jon
008a879e6f docs(user): Use a table to show markdown model tags
ref: #570
2025-02-18 04:12:59 +09:30
Jon
71382a4689 docs: remove links to old notes
ref: #570
2025-02-18 04:12:59 +09:30
Jon
1963bf0817 feat(api): Device Software Viewset requires its own function to obtain the model view serializer
ref: #578 #477
2025-02-18 04:12:59 +09:30
Jon
31a45a1ab1 feat(api): Ticket Comment Viewset requires its own function to obtain the model view serializer
ref: #578 #477
2025-02-18 04:12:59 +09:30
Jon
f7818fe2e8 feat(api): Ticket Viewset requires its own function to obtain the model view serializer
ref: #578 #477
2025-02-18 04:12:59 +09:30
Jon
16388f0a10 feat(api): Always use a models View serializer for the response
ref: #477 #579 closes #578
2025-02-18 04:12:59 +09:30
Jon
2e0b835af9 test(core): Ensure that when parent_ticket changes on a ticket an action comment is created
ref: #495 #558 #577
2025-02-18 04:12:59 +09:30
Jon
81e856967b fix(core): Ensure that if the parent ticket changes, that the logic caters for none
ref: #558 #577
2025-02-18 04:12:59 +09:30
Jon
348b2f270e test(core): Confirm on category change to ticket that an action comment is created
ref: #577 closes #537
2025-02-18 04:12:59 +09:30
Jon
9a4521a604 fix(assistance): Category can be empty for Project Task Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
83dab570a8 fix(assistance): Category can be empty for Problem Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
a189a60e1b fix(assistance): Category can be empty for Incident Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
91bea05496 fix(assistance): Category can be empty for Change Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
ed41e0b8a9 fix(assistance): Category can be empty for Request Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
64ca3415d4 fix(core): Ticket Action comment for category change must use category field
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
ec6bc7d03a feat(core): Add logic to ensure when organization changes, an action comment is created
ref: #577 closes #558
2025-02-18 04:12:59 +09:30
Jon
0d1a0bff19 feat(core): Add logic to ensure when parnet ticket changes, an action comment is created
ref: #558 #577
2025-02-18 04:12:58 +09:30
Jon
cc46a1426d refactor(core): Ticket action comment logic only requires a single check
ref: #577 #558
2025-02-18 04:12:58 +09:30
Jon
30b24a1969 docs(development): remove old notes docs
ref: #570
2025-02-18 04:12:58 +09:30
Jon
ad27501334 chore: update new model tempplate
ref: #605
2025-02-18 04:12:30 +09:30
Jon
1109beb8e9 chore: issue template new model added
ref: #0
2025-02-16 08:43:04 +09:30
8b7550f573 build: bump version 1.10.0 -> 1.10.1 2025-02-14 12:24:21 +00:00
Jon
4f2a28bb52 fix(python): Dont use system TimeZone data, use python zoneinfo module zone data
ref: fixes #603
2025-02-14 21:41:21 +09:30
33c589a1a0 build: bump version 1.9.0 -> 1.10.0 2025-02-10 09:27:27 +00:00
Jon
0a21c37645 fix(core): Dont attempt to access parent_ticket field during ticket validation if it does not exist
ref: #559 #507
2025-02-10 18:42:31 +09:30
Jon
e4dac45d2e Merge pull request #522 from nofusscomputing/feature-next-release 2025-02-10 18:11:16 +09:30
Jon
c9eefc2eb3 Merge pull request #554 from nofusscomputing/ticket-circular-dependency-check 2025-02-10 17:45:07 +09:30
Jon
68d3c1ff26 test(settings): Test User Settings API render to ensure browser_model exists and is the correct type
ref: #554 closes #507
2025-02-10 17:32:59 +09:30
Jon
e5680cdbce test(settings): Test User Settings model to ensure browser_mode field exists
ref: #554 #507
2025-02-10 17:31:42 +09:30
Jon
067dd98eee feat(settings): Provide user with the ability to set browser mode
ref: #554 #481 #507 nofusscomputing/centurion_erp_ui#71
2025-02-10 17:24:52 +09:30
Jon
b153f648f9 feat(core): Parent Ticket validation added to ticket serializer
ref: #554 closes #517
2025-02-10 16:39:00 +09:30
Jon
062e846875 feat(core): Add to ticket endpoint the ability to filter using parent_ticket
ref: #517 #554
2025-02-10 16:19:47 +09:30
Jon
0e1f778b80 feat(core): Add to ticket model a function for circular dependecy check of parent ticket
ref: #517 #554
2025-02-10 16:19:18 +09:30
Jon
aca32b14be Merge pull request #525 from nofusscomputing/model_notes 2025-02-09 22:02:08 +09:30
Jon
16076f0ca3 chore(core): Remove old notes model test suites
ref: #525 #389
2025-02-09 21:48:23 +09:30
Jon
6d69b72368 feat(core): Migrate Notes data to new table
ref: #525 #389 #349 closes #547
2025-02-09 21:47:56 +09:30
Jon
d4c6c08710 chore(core): REmove modelnotes permission from permissions list
ref: #525 #389
2025-02-09 20:55:38 +09:30
Jon
05d4c4a94d refactor: Squash migrations so there is less of them for model notes
ref: #525 #389
2025-02-09 20:40:25 +09:30
Jon
4d5f029913 docs(core): add model notes
ref: #525
2025-02-09 20:04:58 +09:30
Jon
51e7f875a2 test(access): Team Note Model Check object requires org
ref: #525 #527
2025-02-09 20:01:14 +09:30
Jon
b17bc5af75 feat(project_management): Add notes tab to Project Milestone details page
ref: #525 #542
2025-02-09 19:45:11 +09:30
Jon
80db9022b7 feat(itam): Add notes tab to Software Version details page
ref: #525 #541
2025-02-09 19:44:42 +09:30
Jon
288abb5373 feat(itam): Add notes tab to Operating System details page
ref: #525 #531
2025-02-09 19:44:17 +09:30
Jon
f06c9f6091 docs: add model test suite
ref: #525
2025-02-09 19:36:21 +09:30
Jon
d55010d63f test(settings): External Links Note Model Checks
ref: #525 closes #545
2025-02-09 19:35:06 +09:30
Jon
eeb7075f83 test(project_management): Project Type Note Model Checks
ref: #525 closes #544
2025-02-09 19:34:42 +09:30
Jon
1c34fbad08 test(project_management): Project State Note Model Checks
ref: #525 closes #543
2025-02-09 19:34:29 +09:30
Jon
4549379e62 test(project_management): Project Note Model Checks
ref: #525 closes #335
2025-02-09 19:34:12 +09:30
Jon
64fd692d9c test(project_management): Project Milestone Note Model Checks
ref: #525 closes #542
2025-02-09 19:34:00 +09:30
Jon
795f029c75 test(itim): Service Note Model Checks
ref: #525 closes #534
2025-02-09 19:33:33 +09:30
Jon
a091be7b5e test(itim): Port Note Model Checks
ref: #525 closes #536
2025-02-09 19:33:12 +09:30
Jon
a1c9533555 test(itim): Cluster Type Note Model Checks
ref: #525 closes #535
2025-02-09 19:32:47 +09:30
Jon
20f52d856b test(itim): Cluster Note Model Checks
ref: #525 closes #533
2025-02-09 19:32:27 +09:30
Jon
6e296a3713 test(itam): Software Version Note Model Checks
ref: #525 closes #541
2025-02-09 19:31:57 +09:30
Jon
58b73b9b29 test(itam): Software Note Model Checks
ref: #525 closes #532
2025-02-09 19:31:43 +09:30
Jon
cd65d8e93d test(itam): Software Category Note Model Checks
ref: #525 closes #540
2025-02-09 19:31:17 +09:30
Jon
f26b0706ae test(itam): Operating System Version Note Model Checks
ref: #525 closes #539
2025-02-09 19:30:31 +09:30
Jon
1a0839a2c8 test(itam): Operating System Note Model Checks
ref: #525 closes #531
2025-02-09 19:30:14 +09:30
Jon
cc0f92e132 test(itam): Device Type Note Model Checks
ref: #525 closes #538
2025-02-09 19:29:40 +09:30
Jon
b14262fcad test(itam): Device Note Model Checks
ref: #525 closes #524
2025-02-09 19:28:59 +09:30
Jon
1474f47087 test(itam): Device Model Note Model Checks
ref: #525 closes #523
2025-02-09 19:28:46 +09:30
Jon
e56007d4b1 test(core): Manufacturer Note Model Checks
ref: #525 closes #530
2025-02-09 19:28:21 +09:30
Jon
0602070adb test(config_management): Config Group Note Model Checks
ref: #525 closes #529
2025-02-09 19:28:02 +09:30
Jon
760463705b test(assistance): KB Note Model Checks
ref: #525 closes #528
2025-02-09 19:27:37 +09:30
Jon
8eeb5780c6 test(assistance): KB Category Note Model Checks
ref: #525 closes #546
2025-02-09 19:27:25 +09:30
Jon
7b6d6935d4 test(access): Team Note Model Checks
ref: #525 closes #527
2025-02-09 19:27:05 +09:30
Jon
1cec44f7bd test(access): Organization Note Model Checks
ref: #525 closes #526
2025-02-09 19:26:38 +09:30
Jon
51ec6cb0e5 test(core): Model Notes Test Suite
ref: #389 #525
2025-02-09 19:25:36 +09:30
Jon
9b62ba6367 test(settings): Serializer Checks for External Links Notes
ref: #525 #545
2025-02-09 17:58:27 +09:30
Jon
96420f3d23 test(Project_management): Serializer Checks for Project Type Notes
ref: #525 #544
2025-02-09 17:57:59 +09:30
Jon
eb38e70c17 test(Project_management): Serializer Checks for Project State Notes
ref: #525 #543
2025-02-09 17:57:41 +09:30
Jon
dbafbe1a5c test(Project_management): Serializer Checks for Project Notes
ref: #525 #335
2025-02-09 17:57:22 +09:30
Jon
90bee291bd test(Project_management): Serializer Checks for Project Milestone Notes
ref: #525 #542
2025-02-09 17:57:06 +09:30
Jon
5eb120886d test(itim): Serializer Checks for Service Notes
ref: #525 #534
2025-02-09 17:56:31 +09:30
Jon
6e4e63a52e test(itim): Serializer Checks for Port Notes
ref: #525 #536
2025-02-09 17:56:09 +09:30
Jon
8d39fdb9e4 test(itim): Serializer Checks for Cluster Type Notes
ref: #525 #535
2025-02-09 17:55:52 +09:30
Jon
7aa7627faf test(itim): Serializer Checks for Cluster Notes
ref: #525 #533
2025-02-09 17:55:33 +09:30
Jon
29fc7e7e07 test(itam): Serializer Checks for Software Version Notes
ref: #525 #541
2025-02-09 17:55:02 +09:30
Jon
780ab35b26 test(itam): Serializer Checks for Software Notes
ref: #525 #532
2025-02-09 17:54:41 +09:30
Jon
b072ce9fbc test(itam): Serializer Checks for Software Category Notes
ref: #525 #540
2025-02-09 17:54:24 +09:30
Jon
fa757f2edf test(itam): Serializer Checks for Operating System Version Notes
ref: #525 #539
2025-02-09 17:53:43 +09:30
Jon
fccaacbf7c test(itam): Serializer Checks for Operating System Notes
ref: #525 #531
2025-02-09 17:53:27 +09:30
Jon
0f1ff924ed test(itam): Serializer Checks for Device Type Notes
ref: #525 #538
2025-02-09 17:53:02 +09:30
Jon
75598dff94 test(itam): Serializer Checks for Device Notes
ref: #525 #524
2025-02-09 17:52:47 +09:30
Jon
475205f95b test(itam): Serializer Checks for Device Model Notes
ref: #525 #523
2025-02-09 17:52:33 +09:30
Jon
bf74a4c4db test(core): Serializer Checks for Manufacturer Notes
ref: #525 #530
2025-02-09 17:52:07 +09:30
Jon
c15966c694 test(config_management): Serializer Checks for Config Groups Notes
ref: #525 #529
2025-02-09 17:51:18 +09:30
Jon
56ced08c4d test(assistance): Serializer Checks for KB Notes
ref: #525 #528
2025-02-09 17:50:53 +09:30
Jon
c8d6135b83 test(assistance): Serializer Checks for KB Category Notes
ref: #525 #546
2025-02-09 17:50:39 +09:30
Jon
98159c1840 test(access): Serializer Checks for Team Notes
ref: #525 #527
2025-02-09 17:50:03 +09:30
Jon
8140c1f945 test(access): Serializer Checks for Organization Notes
ref: #525 #526
2025-02-09 17:49:51 +09:30
Jon
eff1255419 feat(core): Ensure when editing a model note, the modified user is updated.
ref: #389
2025-02-09 16:47:13 +09:30
Jon
8f2bf3d71e test(core): Test Suite for Model Notes checks
ref: #49 #389 #525
2025-02-09 16:47:02 +09:30
Jon
1a7bade7f3 docs(development): Model Notes Creation
ref: #49 #389 #525
2025-02-09 00:26:55 +09:30
Jon
a7912531e3 test(api): API Fileds user to be super user for tests to run
ref: #525
2025-02-08 23:10:06 +09:30
Jon
32c6991730 test(settings): External Links Notes Function Viewset Tests
ref: #525 #545
2025-02-08 23:08:27 +09:30
Jon
98c02c4101 test(project_management): Project Type Notes Function Viewset Tests
ref: #525 #544
2025-02-08 23:08:01 +09:30
Jon
d8b12aeb7e test(project_management): Project State Notes Function Viewset Tests
ref: #525 #543
2025-02-08 23:07:44 +09:30
Jon
5b02f2c751 test(project_management): Project Notes Function Viewset Tests
ref: #525 #335
2025-02-08 23:07:27 +09:30
Jon
0d4ad05ac9 test(project_management): Project Milestone Notes Function Viewset Tests
ref: #525 #542
2025-02-08 23:07:13 +09:30
Jon
def43c1134 test(itim): Service Notes Function Viewset Tests
ref: #525 #534
2025-02-08 23:06:38 +09:30
Jon
3c08580c10 test(itim): Port Notes Function Viewset Tests
ref: #525 #536
2025-02-08 23:06:19 +09:30
Jon
bc329d2453 test(itim): Cluster Types Notes Function Viewset Tests
ref: #525 #535
2025-02-08 23:05:48 +09:30
Jon
72dfac9d22 test(itim): Cluster Notes Function Viewset Tests
ref: #525 #533
2025-02-08 23:05:27 +09:30
Jon
a74550e1f3 test(itam): Software Version Notes Function Viewset Tests
ref: #525 #541
2025-02-08 22:32:05 +09:30
Jon
382b1d2a30 test(itam): Software Notes Function Viewset Tests
ref: #525 #532
2025-02-08 22:31:48 +09:30
Jon
9c2ef50c3b test(itam): Software Category Notes Function Viewset Tests
ref: #525 #540
2025-02-08 22:31:34 +09:30
Jon
3536a59e5a test(itam): Operating System Version Notes Function Viewset Tests
ref: #525 #539
2025-02-08 22:31:07 +09:30
Jon
0213d45f8a test(itam): Operating System Notes Function Viewset Tests
ref: #525 #531
2025-02-08 22:30:47 +09:30
Jon
41cf9aae41 test(itam): Device Type Notes Function Viewset Tests
ref: #525 #538
2025-02-08 22:30:16 +09:30
Jon
0b25a85e26 test(itam): Device Notes Function Viewset Tests
ref: #525 #524
2025-02-08 22:30:01 +09:30
Jon
4caf26686c test(itam): Device Model Notes Function Viewset Tests
ref: #525 #523
2025-02-08 22:29:49 +09:30
Jon
58c19388c7 test(core): Manufacturer Notes Function Viewset Tests
ref: #525 #530
2025-02-08 22:29:27 +09:30
Jon
6a211677a8 test(config_management): Config Groups Notes Function Viewset Tests
ref: #525 #529
2025-02-08 22:29:07 +09:30
Jon
45d05a183d test(assistance): Knowledge Base Notes Function Viewset Tests
ref: #525 #528
2025-02-08 21:47:06 +09:30
Jon
f144129b4d test(assistance): Knowledge Base Category Notes Function Viewset Tests
ref: #525 #546
2025-02-08 21:46:50 +09:30
Jon
e038d08544 test(access): Team Notes Function Viewset Tests
ref: #525 #527
2025-02-08 21:46:24 +09:30
Jon
05c9e3f066 test(access): Organization Notes Function Viewset Tests
ref: #525 #526
2025-02-08 21:46:10 +09:30
Jon
a1851918e3 fix(core): Permissions require the parent model for model notes
ref: #389 #525
2025-02-08 21:45:09 +09:30
Jon
136f907cd5 docs(access): Add type to parent_model attribute within Organization Mixin
ref: #389 #525
2025-02-08 21:44:08 +09:30
Jon
969aaea884 test(core): Model Notes Test Cases
ref: #389 #525 closes #49
2025-02-08 20:45:41 +09:30
Jon
5a07892b25 test: Remove old notes model tests
ref: #49 #389 #525
2025-02-08 19:52:24 +09:30
Jon
5d17e1a49c test(settings): External Notes Test Cases for ViewSet
ref: #525 #545
2025-02-08 19:31:52 +09:30
Jon
185fb6c332 test(project_management): Project Type Notes Test Cases for ViewSet
ref: #525 #544
2025-02-08 19:30:06 +09:30
Jon
e690012da8 test(project_management): Project State Notes Test Cases for ViewSet
ref: #525 #543
2025-02-08 19:29:29 +09:30
Jon
76b6a57583 test(project_management): Project Notes Test Cases for ViewSet
ref: #525 #335
2025-02-08 19:29:11 +09:30
Jon
7bea5a6156 test(project_management): Project Milestone Notes Test Cases for ViewSet
ref: #525 #542
2025-02-08 19:28:59 +09:30
Jon
45b2f083de test(itim): Service Notes Test Cases for ViewSet
ref: #525 #534
2025-02-08 19:27:52 +09:30
Jon
643894cdfc test(itim): Port Notes Test Cases for ViewSet
ref: #525 #536
2025-02-08 19:27:36 +09:30
Jon
443cbce73b test(itim): Cluster Type Notes Test Cases for ViewSet
ref: #525 #535
2025-02-08 19:27:16 +09:30
Jon
9a5f4e90ff test(itim): Cluster Notes Test Cases for ViewSet
ref: #525 #533
2025-02-08 19:26:55 +09:30
Jon
9c7e572e00 test(itam): Software Version Notes Test Cases for ViewSet
ref: #525 #541
2025-02-08 19:26:34 +09:30
Jon
876a1622dd test(itam): Software Notes Test Cases for ViewSet
ref: #525 #532
2025-02-08 19:25:55 +09:30
Jon
161751f97f test(itam): Software Category Notes Test Cases for ViewSet
ref: #525 #540
2025-02-08 19:25:43 +09:30
Jon
ac0e756c77 test(itam): Operating System Version Notes Test Cases for ViewSet
ref: #525 #539
2025-02-08 19:25:14 +09:30
Jon
115159b8a6 test(itam): Operating_system Notes Test Cases for ViewSet
ref: #525 #531
2025-02-08 19:24:50 +09:30
Jon
10e2550621 test(itam): Device Type Notes Test Cases for ViewSet
ref: #525 #538
2025-02-08 19:24:29 +09:30
Jon
edb6aa6eb7 test(itam): Device Notes Test Cases for ViewSet
ref: #525 #524
2025-02-08 19:23:58 +09:30
Jon
2534557105 test(itam): Device Model Notes Test Cases for ViewSet
ref: #525 #523
2025-02-08 19:23:46 +09:30
Jon
2e6e73353e test(core): Manufacturer Notes Test Cases for ViewSet
ref: #525 #530
2025-02-08 19:23:16 +09:30
Jon
37d6ba202d test(config_management): Config Groups Notes Test Cases for ViewSet
ref: #525 #529
2025-02-08 19:22:55 +09:30
Jon
f3a4123fb6 test(assistance): Knowledge Base Notes Test Cases for ViewSet
ref: #525 #528
2025-02-08 19:22:23 +09:30
Jon
b20abd82e1 test(assistance): Knowledge Base Category Notes Test Cases for ViewSet
ref: #525 #546
2025-02-08 19:22:07 +09:30
Jon
d11e7fa71d test(access): Team Notes Test Cases for ViewSet
ref: #525 #527
2025-02-08 19:21:28 +09:30
Jon
96a8a6a158 test(access): Organization Notes Test Cases for ViewSet
ref: #525 #526
2025-02-08 19:21:15 +09:30
Jon
c0695c8c84 test(project_management): Correct kwargs for Project Milestone Notes Test Cases for API Field Checks
ref: #525 #542
2025-02-08 17:58:44 +09:30
Jon
64c2f1b0d9 test(assistance): Knowledge Base Category Notes Test Cases for API Field Checks
ref: #525 #546
2025-02-08 17:58:33 +09:30
Jon
37a8368a4e feat(assistance): Knowledge Base Category Notes viewset
ref: #525 #546
2025-02-08 17:43:51 +09:30
Jon
ba9c811e16 feat(assistance): Knowledge Base Category Notes Serializer
ref: #525 #546
2025-02-08 17:43:25 +09:30
Jon
ae47b06a72 feat(assistance): Knowledge Base Category Notes Model
ref: #525 #546
2025-02-08 17:43:09 +09:30
Jon
bc8815d607 test(Settings): External Link Notes Test Cases for API Field Checks
ref: #525 #545
2025-02-08 17:33:57 +09:30
Jon
8a06a0dd3a test(project_management): Project Type Notes Test Cases for API Field Checks
ref: #525 #544
2025-02-08 17:33:33 +09:30
Jon
46381fc138 test(project_management): Project State Notes Test Cases for API Field Checks
ref: #525 #543
2025-02-08 17:33:18 +09:30
Jon
be2e0ae837 test(project_management): Project Notes Test Cases for API Field Checks
ref: #335 #525
2025-02-08 17:32:54 +09:30
Jon
cc34e43262 test(project_management): Project Milestone Notes Test Cases for API Field Checks
ref: #525 #542
2025-02-08 17:32:27 +09:30
Jon
a3d0b10a75 test(itim): Service Notes Test Cases for API Field Checks
ref: #525 #534
2025-02-08 17:31:59 +09:30
Jon
eafc364ba5 test(itim): Port Notes Test Cases for API Field Checks
ref: #525 #536
2025-02-08 17:31:44 +09:30
Jon
0304010d0e test(itim): Cluster Types Notes Test Cases for API Field Checks
ref: #525 #535
2025-02-08 17:31:26 +09:30
Jon
5d3eadfd56 test(itim): Cluster Notes Test Cases for API Field Checks
ref: #525 #533
2025-02-08 17:31:09 +09:30
Jon
fe7e9169be test(itam): Software Version Notes Test Cases for API Field Checks
ref: #525 #541
2025-02-08 17:30:43 +09:30
Jon
9d109d2f9a test(itam): Software Notes Test Cases for API Field Checks
ref: #525 #532
2025-02-08 17:30:27 +09:30
Jon
f9b0b694f9 test(itam): Software Category Notes Test Cases for API Field Checks
ref: #525 #540
2025-02-08 17:28:57 +09:30
Jon
d19e742cc5 test(itam): Device Type Notes Test Cases for API Field Checks
ref: #525 #538
2025-02-08 17:27:13 +09:30
Jon
a077506eb0 test(itam): Device Notes Test Cases for API Field Checks
ref: #524 #525
2025-02-08 17:26:49 +09:30
Jon
585f1c01e8 test(itam): Device Model Notes Test Cases for API Field Checks
ref: #523 #525
2025-02-08 17:26:35 +09:30
Jon
f84759720c test(itam): Operating System Test Cases for API Field Checks
ref: #525 #531
2025-02-08 17:26:08 +09:30
Jon
91c5216120 test(itam): Operating System Version Test Cases for API Field Checks
ref: #525 #539
2025-02-08 17:25:47 +09:30
Jon
98868aab32 test(core): Manufacturer Test Cases for API Field Checks
ref: #525 #530
2025-02-08 17:25:00 +09:30
Jon
0a8c3cfffb test(config_management): Config Group Test Cases for API Field Checks
ref: #525 #529
2025-02-08 17:24:34 +09:30
Jon
bff5ea3fe8 test(assistance): KB Test Cases for API Field Checks
ref: #525 #528
2025-02-08 17:24:07 +09:30
Jon
e7729afc98 test(access): Team Test Cases for API Field Checks
ref: #525 #527
2025-02-08 17:23:14 +09:30
Jon
7358b0a90c test(access): Organization Test Cases for API Field Checks
ref: #525 #526
2025-02-08 17:22:59 +09:30
Jon
e67ede0e74 test(core): Model Notes Base Test Cases for API Field Checks
ref: #389 #525
2025-02-08 00:05:18 +09:30
Jon
1e914d1345 feat(project_management): Project Type Notes ViewSet
ref: #525 #544
2025-02-07 23:09:06 +09:30
Jon
ba7c0cd117 feat(project_management): Project Type Notes Serializer
ref: #525 #544
2025-02-07 23:08:34 +09:30
Jon
029e5a6d08 feat(project_management): Project Type Notes Model
ref: #525 #544
2025-02-07 23:08:17 +09:30
Jon
f71fe3d6d7 feat(project_management): Project State Notes ViewSet
ref: #525 #543
2025-02-07 23:00:34 +09:30
Jon
c97cdf276f feat(project_management): Project State Notes Serializer
ref: #525 #543
2025-02-07 22:59:56 +09:30
Jon
c2f7e25579 feat(project_management): Project State Notes Model
ref: #525 #543
2025-02-07 22:59:38 +09:30
Jon
9e7f731a0e feat(project_management): Project Milestone Notes ViewSet
ref: #525 #542
2025-02-07 22:52:59 +09:30
Jon
1f366ab8fd feat(project_management): Project Milestone Notes Serializer
ref: #525 #542
2025-02-07 22:52:20 +09:30
Jon
e59e808b7f feat(project_management): Project Milestone Notes Model
ref: #525 #542
2025-02-07 22:52:01 +09:30
Jon
39f48aadb1 feat(itam): Software Version Notes ViewSet
ref: #525 #541
2025-02-07 22:41:32 +09:30
Jon
e121741a6c feat(itam): Software Version Notes Serializer
ref: #525 #541
2025-02-07 22:40:57 +09:30
Jon
0145ee9349 feat(itam): Software Version Notes Model
ref: #525 #541
2025-02-07 22:40:41 +09:30
Jon
7a0be93752 feat(itam): Software Category Notes ViewSet
ref: #525 #540
2025-02-07 22:32:14 +09:30
Jon
89eecad448 feat(itam): Software Category Notes Serializer
ref: #525 #540
2025-02-07 22:31:45 +09:30
Jon
a5cbf108cb feat(itam): Software Category Notes Model
ref: #525 #540
2025-02-07 22:31:29 +09:30
Jon
67e892dbaa feat(itam): Operating System Version Notes ViewSet
ref: #525 #539
2025-02-07 22:08:12 +09:30
Jon
8477700125 feat(itam): Operating System Version Notes Serializer
ref: #525 #539
2025-02-07 22:07:39 +09:30
Jon
59603e0455 feat(itam): Operating System Version Notes Model
ref: #525 #539
2025-02-07 22:07:22 +09:30
Jon
0bc3f420f0 test: remove old notes model tests
ref: #389 #526
2025-02-07 21:47:01 +09:30
Jon
960646a68c feat(settings): External Link Notes ViewSet
ref: #526 #545
2025-02-07 21:42:09 +09:30
Jon
d1dbe965f9 feat(settings): External Link Notes Serializer
ref: #526 #545
2025-02-07 21:41:40 +09:30
Jon
9fa6ec46dd feat(settings): External Link Notes Model
ref: #526 #545
2025-02-07 21:41:19 +09:30
Jon
d03afa3017 feat(itam): Device Model Notes ViewSet
ref: #523 #526
2025-02-07 21:32:06 +09:30
Jon
f157545b01 feat(itam): Device Model Notes Serializer
ref: #523 #526
2025-02-07 21:31:24 +09:30
Jon
e18f70a20c feat(itam): Device Model Notes Model
ref: #523 #526
2025-02-07 21:31:09 +09:30
Jon
e8ce851c31 fix(access): field organization requires team related_model for org
ref: #526
2025-02-07 21:23:14 +09:30
Jon
4dd4215fd0 test: Update url_name to match new notes endpoint
ref: #526
2025-02-07 21:17:24 +09:30
Jon
dad843f1d4 test(config_Management): Update url_name to match new notes endpoint
ref: #526 #529
2025-02-07 21:11:43 +09:30
Jon
47b1dd7732 feat(itam): Device Type Notes ViewSet
ref: #526 #538
2025-02-07 21:08:55 +09:30
Jon
a599cc9f9c feat(itam): Device Type Notes Serializer
ref: #526 #538
2025-02-07 21:08:19 +09:30
Jon
893066942d feat(itam): Device Type Notes Model
ref: #526 #538
2025-02-07 21:08:05 +09:30
Jon
858538e3eb feat(core): Create an action comment on a ticket when the category changes
ref: #526 #537
2025-02-07 20:50:45 +09:30
Jon
b42a5a9abb feat(itim): Porte Notes ViewSet
ref: #526 #536
2025-02-07 20:35:27 +09:30
Jon
9ea8422007 feat(itim): Porte Notes Serializer
ref: #526 #536
2025-02-07 20:35:01 +09:30
Jon
39fdc9ef1d feat(itim): Porte Notes Model
ref: #526 #536
2025-02-07 20:34:48 +09:30
Jon
a161677f79 feat(itim): Cluster Type Notes ViewSet
ref: #526 #535
2025-02-07 20:28:56 +09:30
Jon
013c31272b feat(itim): Cluster Type Notes Serializer
ref: #526 #535
2025-02-07 20:28:17 +09:30
Jon
911a086963 feat(itim): Cluster Type Notes Model
ref: #526 #535
2025-02-07 20:28:06 +09:30
Jon
c1a47045e7 feat(project_management): Project Notes ViewSet
ref: #335 #526
2025-02-07 20:21:16 +09:30
Jon
644188f952 feat(project_management): Project Notes Serializer
ref: #335 #526
2025-02-07 20:20:46 +09:30
Jon
717bc5750b feat(project_management): Project Notes Model
ref: #335 #526
2025-02-07 20:20:30 +09:30
Jon
3ca1aa3756 feat(itim): Service Notes ViewSet
ref: #526 #534
2025-02-07 20:12:26 +09:30
Jon
afd20ac4e3 feat(itim): Service Notes Serializer
ref: #526 #534
2025-02-07 20:12:01 +09:30
Jon
79c96ff2b5 feat(itim): Service Notes Model
ref: #526 #534
2025-02-07 20:11:48 +09:30
Jon
b135b0690e feat(itim): Cluster Notes ViewSet
ref: #526 #533
2025-02-07 19:59:56 +09:30
Jon
041b837068 feat(itim): Cluster Notes Serializer
ref: #526 #533
2025-02-07 19:59:19 +09:30
Jon
ea8a56322f feat(itim): Cluster Notes Model
ref: #526 #533
2025-02-07 19:59:05 +09:30
Jon
0b418b6f2c feat(itam): Software Notes ViewSet
ref: #526 #532
2025-02-07 19:47:35 +09:30
Jon
a2abfc83ee feat(itam): Software Notes Serilaizer
ref: #526 #532
2025-02-07 19:46:54 +09:30
Jon
03ce6824ee feat(itam): Software Notes Model
ref: #526 #532
2025-02-07 19:46:39 +09:30
Jon
c12f9925ae feat(itam): Operating System Notes ViewSet
ref: #526 #531
2025-02-07 19:36:56 +09:30
Jon
3efa835295 feat(itam): Operating System Notes Serializer
ref: #526 #531
2025-02-07 19:36:10 +09:30
Jon
d8fdb17112 feat(itam): Operating System Notes Model
ref: #526 #531
2025-02-07 19:36:00 +09:30
Jon
5842e81455 feat(core): Manufacturer Notes viewset
ref: #526 #530
2025-02-07 19:18:16 +09:30
Jon
f057693a61 feat(core): Manufacturer Notes serializer
ref: #526 #530
2025-02-07 19:17:53 +09:30
Jon
c77d33e910 feat(core): Manufacturer Notes Model
ref: #526 #530
2025-02-07 19:17:37 +09:30
Jon
ecb7116f4a feat(config_management): Config Group Notes ViewSet
ref: #526 #529
2025-02-07 19:06:34 +09:30
Jon
3464fcf93b feat(config_management): Config Group Notes Serializer
ref: #526 #529
2025-02-07 19:06:11 +09:30
Jon
c8f0c54549 feat(config_management): Config Group Notes Model
ref: #526 #529
2025-02-07 19:05:57 +09:30
Jon
4367878396 feat(assistance): Knowledge Base Notes ViewSet
ref: #526 #528
2025-02-07 18:49:56 +09:30
Jon
c950daa011 feat(assistance): Knowledge Base Notes Serializer
ref: #526 #528
2025-02-07 18:49:34 +09:30
Jon
725b009ee6 feat(assistance): Knowledge Base Notes Model
ref: #526 #528
2025-02-07 18:49:22 +09:30
Jon
9fcb793bb2 feat(access): Team Notes ViewSet
ref: #526 #527
2025-02-07 18:35:38 +09:30
Jon
bd574ce952 feat(access): Team Notes Serializer
ref: #526 #527
2025-02-07 18:35:10 +09:30
Jon
77f0554893 feat(access): Team Notes Model
ref: #526 #527
2025-02-07 18:34:37 +09:30
Jon
e07af389ba feat(access): Organization Notes ViewSet
ref: #525 #526
2025-02-07 18:17:05 +09:30
Jon
3060fd0b86 feat(access): Organization Notes Serializer
ref: #525 #526
2025-02-07 18:16:27 +09:30
Jon
3184e5fb07 feat(access): Organization Notes Model
ref: #525 #526
2025-02-07 18:16:17 +09:30
Jon
02140ce731 feat(itam): Device Notes ViewSet
ref: #524 #525
2025-02-07 18:12:50 +09:30
Jon
dd06d9e1ff feat(itam): Device Notes Serializer
ref: #524 #525
2025-02-07 18:12:21 +09:30
Jon
ca4e9add88 feat(itam): Device Notes Model
ref: #524 #525
2025-02-07 18:12:09 +09:30
Jon
657bbfb25a test(core): Remove notes test cases for previous notes model
ref: #389 #525
2025-02-07 18:11:10 +09:30
Jon
64e344206c refactor(access): Dont add releationship from tenancyObject.organization to organization model
ref: #389 #525
2025-02-07 18:10:44 +09:30
Jon
a132baa838 feat(core): Base viewset for model notes
ref: #389 #525
2025-02-07 18:08:42 +09:30
Jon
f833b93074 feat(core): Base serializer for model notes
ref: #389 #525
2025-02-07 18:06:49 +09:30
Jon
06991853ff fix(core): Use generic APIError for ticket save when no action comment will be created
ref: #525
2025-02-07 16:32:37 +09:30
Jon
cff343989b feat(core): Base model for model notes
ref: #389 #525
2025-02-07 15:49:12 +09:30
Jon
ff8d422308 feat(core): Add failsafe to throw an exception if no action comment will be created
ref: #522 closes #492
2025-02-06 23:08:11 +09:30
Jon
aded110307 feat(core): Add field parent_ticket to base ticket view serializer
ref: #517 #522
2025-02-06 22:50:06 +09:30
Jon
fbc6a3c338 feat(project_management): Add field parent_ticket to project task ticket view serializer
ref: #517 #522
2025-02-06 22:49:42 +09:30
Jon
7629c6cf9b feat(itim): Add field parent_ticket to problem ticket view serializer
ref: #517 #522
2025-02-06 22:49:24 +09:30
Jon
96b2eec050 feat(itim): Add field parent_ticket to incident ticket view serializer
ref: #517 #522
2025-02-06 22:49:15 +09:30
Jon
30394644bf feat(itim): Add field parent_ticket to change ticket view serializer
ref: #517 #522
2025-02-06 22:49:02 +09:30
Jon
610df69e21 feat(assistance): Add field parent_ticket to request ticket view serializer
ref: #517 #522
2025-02-06 22:48:46 +09:30
Jon
0b0b527b55 feat(core): Add field parent to ticket model
ref: #517 #522
2025-02-06 22:40:20 +09:30
1308 changed files with 70247 additions and 15908 deletions

View File

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

121
.github/ISSUE_TEMPLATE/new_model.md vendored Normal file
View File

@ -0,0 +1,121 @@
---
name: New Database Model
about: Use when creating a new database model.
title: "New Model - <model table name>"
type: Task
labels: task::feature, triage, type::task
---
<!-- Add an intro -->
<!-- describe a use case if not covered in intro -->
## 📝 Details
<!--
Describe in detail the following:
- New model field
- if foreign key field, what it's name will be or if it's not to be linked ensure specified and coded with `related_name = '+' to disable the link`.
- How the UI will work, be layed out, new ui features etc
- custom permissions if required
-->
## 🚧 Tasks
<!-- Don't remove tasks strike them out. use `~~` before and after the item. i.e. `- ~~[ ] Model Created~~` note: don't include the list dash-->
- [ ] 🆕 [Model Created](https://nofusscomputing.com/projects/centurion_erp/development/models/)
- [ ] 🛠️ 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
- [ ] 📘 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`](https://nofusscomputing.com/projects/centurion_erp/development/models/#history) set to correct application label
- [ ] Model class variable [`history_model_name`](https://nofusscomputing.com/projects/centurion_erp/development/models/#history) set to correct model label
- [ ] 📓 New [Notes model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/) created
- [ ] 🆕 Model Created
- [ ] 🛠️ 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)
- [ ] Admin Documentation added/updated _if applicable_
- [ ] Developer Documentation added/updated _if applicable_
- [ ] User Documentation added/updated
---
<!-- Add additional tasks here and as a check box list -->
### 🧪 Tests
- Unit Tests
- [ ] API Render (fields)
- [ ] [Model](https://nofusscomputing.com/projects/centurion_erp/development/models/#tests)
- [ ] ViewSet
- Function Test
- [ ] History API Render (fields)
- [ ] History Entries
- [ ] API Metadata
- [ ] API Permissions
- [ ] Model
- [ ] Serializer
- [ ] ViewSet
## ✅ Requirements
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 -->

View File

@ -20,7 +20,7 @@
<!-- dont remove tasks below strike through including the checkbox by enclosing in double tidle '~~' -->
- [ ] **Feature Release ONLY** :red_square: Squash migration files :red_square:
- [ ] **Feature Release ONLY** :red_square: [Squash migration files](https://docs.djangoproject.com/en/5.2/topics/migrations/#squashing-migrations) :red_square:
_Multiple migration files created as part of this release are to be sqauashed into a few files as possible so as to limit the number of migrations_
- [ ] :firecracker: Contains breaking-change Any Breaking change(s)?

8
.gitignore vendored
View File

@ -1,9 +1,10 @@
venv/**
*/static/**
__pycache__
**.sqlite3
**.sqlite*
**.sqlite
**.coverage
.coverage*
artifacts/
**.tmp.*
volumes/
@ -15,3 +16,8 @@ node_modules/
package-lock.json
package.json
**.junit.xml
**.JUnit.xml
feature_flags.json
coverage_*.json
*-coverage.xml
log/

45
.vscode/launch.json vendored
View File

@ -17,7 +17,6 @@
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Debug: Gunicorn",
"type": "debugpy",
"request": "launch",
@ -31,7 +30,6 @@
"--bind",
"0.0.0.0:8002",
"app.wsgi:application",
],
"django": true,
"autoStartBrowser": false,
@ -40,6 +38,18 @@
"PROMETHEUS_MULTIPROC_DIR": ""
}
},
{
"name": "Centurion Feature Flag (Management Command)",
"type": "debugpy",
"request": "launch",
"args": [
"feature_flag",
// "0.0.0.0:8002"
],
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Migrate",
"type": "debugpy",
@ -50,7 +60,6 @@
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Debug: Celery",
@ -68,6 +77,34 @@
"debug-itsm@%h"
],
"cwd": "${workspaceFolder}/app"
}
},
{
"name": "Debug pytest (collect)",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"--override-ini", "addopts=",
"--collect-only",
"app",
],
"console": "integratedTerminal",
"justMyCode": false
},
{
"name": "Python Debugger: Local Attach",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
]
},
]
}

15
.vscode/settings.json vendored
View File

@ -5,18 +5,23 @@
"!python"
],
"python.testing.pytestArgs": [
// "-v",
// "--cov",
// "--cov-report xml",
"-s",
"--override-ini", "addopts=",
"app",
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"testing.coverageToolbarEnabled": true,
"cSpell.words": [
"ITSM"
],
"cSpell.language": "en-AU",
"jest.enable": false,
"pylint.enabled": true,
"testing.showCoverageInExplorer": true,
"testing.coverageToolbarEnabled": true,
"testing.coverageBarThresholds": {
"red": 0,
"yellow": 60,
"green": 90
},
"telemetry.feedback.enabled": false,
}

File diff suppressed because it is too large Load Diff

View File

@ -95,6 +95,34 @@ clear; \
```
## Tips / Handy info
- To obtain a list of models _(in in the same order as the file system)_ using the db shell `python3 manage.py dbshell` run the following sql command:
``` sql
SELECT model FROM django_content_type ORDER BY app_label ASC, model ASC;
```
# Old working docs

View File

@ -1,3 +1,137 @@
## 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.
- Ticket and Ticket Comment added behind feature flag `2025-00006` and will remain behind this flag until production ready.
- In preparation of the [Ticket and Ticket Comment model re-write](https://github.com/nofusscomputing/centurion_erp/issues/564)
- Depreciated Change Ticket
- Depreciated Ticket Comment Endpoint
- Depreciated Request Ticket
- Depreciated Incident Ticket
- Depreciated Problem Ticket
- Depreciated Project Task Ticket
These endpoints still work and will remain so until the new Ticket and Ticket Comment Models are production ready.
## Version 1.15.0
- Entities model added behind feature flag `2025-00002` and will remain behind this flag until production ready.
- Roles model added behind feature flag `2025-00003` and will remain behind this flag until production ready.
- Accounting Module added behind feature flag `2025-00004` and will remain behind this flag until production ready.
## Version 1.14.0
- Git Repository and Git Group Models added behind feature flag `2025-00001`. They will remain behind this feature flag until the Git features are fully developed and ready for use.
## Version 1.13.0
- DevOps Module added.
- Feature Flagging Component added as par of the DevOps module.
## Version 1.11.0
**Note:** Migrations should be performed offline. **Failing to perform** an online migration, the option provided below will not be available if the migration crashes. Running the below commands to reset the database for the migrations to re-run will cause data loss if users are making changes to Centurion.
- History views removed from original Centurion interface.
- History views removed from API v1.
- A migration exists that will move the history from the old tables to the new ones.
if for some reason the migration crashes enter the following commands in the dbshell `python manage.py dbshell` and restart the migrations
``` sql
delete from access_organization_history;
delete from access_team_history;
delete from assistance_knowledge_base_history;
delete from assistance_knowledge_base_category_history;
delete from config_management_configgroups_history;
delete from config_management_configgroupsoftware_history;
delete from config_management_configgrouphosts_history;
delete from core_manufacturer_history;
delete from core_ticketcategory_history;
delete from core_ticketcommentcategory_history;
delete from itam_device_history;
delete from itam_devicemodel_history;
delete from itam_devicetype_history;
delete from itam_deviceoperatingsystem_history;
delete from itam_devicesoftware_history;
delete from itam_operatingsystem_history;
delete from itam_operatingsystemversion_history;
delete from itam_software_history;
delete from itam_softwareversion_history;
delete from itam_softwarecategory_history;
delete from itim_cluster_history;
delete from itim_clustertype_history;
delete from itim_port_history;
delete from itim_service_history;
delete from project_management_project_history;
delete from project_management_projectmilestone_history;
delete from project_management_projectstate_history;
delete from project_management_projecttype_history;
delete from settings_externallink_history;
delete from core_model_history;
```
The above commands truncate the data from the new history tables so the migration can run again.
## Version 1.10.0
- Nothing significant to report
## Version 1.9.0
- Nothing significant to report
## Version 1.8.0
- Prometheus exporter added. To enable metrics for the database you will have to update the database backend. see the [docs](https://nofusscomputing.com/projects/centurion_erp/administration/monitoring/#django-exporter-setup) for further information.

View File

@ -1,17 +0,0 @@
[run]
source = .
omit =
*migrations/*
*tests/*/*
[report]
omit =
*/tests/*/*
*/migrations/*
*apps.py
*manage.py
*__init__.py
*asgi*
*wsgi*
*admin.py
*urls.py

View File

@ -1,8 +1,14 @@
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 .models import *
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)

View File

@ -3,7 +3,7 @@ from django.db.models import Q
from app import settings
from access.models import Organization
from access.models.tenant import Tenant as Organization
from core.forms.common import CommonModelForm

View File

@ -4,7 +4,7 @@ from django.forms import inlineformset_factory
from .team_users import TeamUsersForm, TeamUsers
from access.models import Team
from access.models.team import Team
from access.functions import permissions
from app import settings

View File

@ -2,7 +2,7 @@ from django.db.models import Q
from app import settings
from access.models import TeamUsers
from access.models.team_user import TeamUsers
from core.forms.common import CommonModelForm

View File

@ -9,10 +9,13 @@ def permission_queryset():
apps = [
'access',
'accounting',
'assistance',
'config_management',
'core',
'devops',
'django_celery_results',
'human_resources',
'itam',
'itim',
'project_management',
@ -24,19 +27,32 @@ def permission_queryset():
'chordcounter',
'comment',
'groupresult',
'history',
'modelnotes',
'usersettings',
]
exclude_permissions = [
'add_checkin',
'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',
]
return Permission.objects.filter(
return Permission.objects.select_related('content_type').filter(
content_type__app_label__in=apps,
).exclude(
content_type__model__in=exclude_models

View File

@ -1,15 +1,22 @@
import django
from django.contrib.auth.middleware import (
AuthenticationMiddleware,
SimpleLazyObject,
partial,
)
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group
from django.utils.deprecation import MiddlewareMixin
from access.models import Organization, Team
from access.models.tenant import Tenant as Organization
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

View File

@ -44,7 +44,7 @@ class Migration(migrations.Migration):
('team_name', models.CharField(default='', max_length=50, verbose_name='Name')),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists])),
],
options={
'verbose_name_plural': 'Teams',

View File

@ -59,7 +59,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='team',
name='organization',
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
),
migrations.AlterField(
model_name='team',

View File

@ -0,0 +1,49 @@
# Generated by Django 5.1.5 on 2025-02-09 11:07
import access.models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0002_alter_organization_options_alter_team_options_and_more'),
('core', '0012_modelnotes'),
]
operations = [
migrations.AlterField(
model_name='team',
name='organization',
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.team.Team.validatate_organization_exists], verbose_name='Organization'),
),
migrations.CreateModel(
name='OrganizationNotes',
fields=[
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.organization', verbose_name='Model')),
],
options={
'verbose_name': 'Organization Note',
'verbose_name_plural': 'Organization Notes',
'db_table': 'access_organization_notes',
'ordering': ['-created'],
},
bases=('core.modelnotes',),
),
migrations.CreateModel(
name='TeamNotes',
fields=[
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.team', verbose_name='Model')),
],
options={
'verbose_name': 'Team Note',
'verbose_name_plural': 'Team Notes',
'db_table': 'access_team_notes',
'ordering': ['-created'],
},
bases=('core.modelnotes',),
),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 5.1.5 on 2025-02-20 13:25
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0003_alter_team_organization_organizationnotes_teamnotes'),
('core', '0015_modelhistory_manufacturerhistory_and_more'),
]
operations = [
migrations.CreateModel(
name='OrganizationHistory',
fields=[
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.organization', verbose_name='Model')),
],
options={
'verbose_name': 'Organization History',
'verbose_name_plural': 'Organization History',
'db_table': 'access_organization_history',
'ordering': ['-created'],
},
bases=('core.modelhistory',),
),
migrations.CreateModel(
name='TeamHistory',
fields=[
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.team', verbose_name='Model')),
],
options={
'verbose_name': 'Team History',
'verbose_name_plural': 'Team History',
'db_table': 'access_team_history',
'ordering': ['-created'],
},
bases=('core.modelhistory',),
),
]

View File

@ -0,0 +1,140 @@
# Generated by Django 5.2 on 2025-04-10 02:34
import access.fields
import access.models.tenancy
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0004_organizationhistory_teamhistory'),
('auth', '0012_alter_user_first_name_max_length'),
('core', '0021_alter_ticketlinkeditem_item_type'),
]
operations = [
migrations.CreateModel(
name='Entity',
fields=[
('is_global', models.BooleanField(default=False, help_text='Is this a global object?', verbose_name='Global Object')),
('model_notes', models.TextField(blank=True, default=None, help_text='Tid bits of information', null=True, verbose_name='Notes')),
('id', models.AutoField(help_text='Primary key of the entry', primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('entity_type', models.CharField(default='entity', help_text='Type this entity is', max_length=30, verbose_name='Entity Type')),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of creation', verbose_name='Created')),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of last modification', verbose_name='Modified')),
('organization', models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Organization')),
],
options={
'verbose_name': 'Entity',
'verbose_name_plural': 'Entities',
'ordering': ['created', 'modified', 'organization'],
},
),
migrations.CreateModel(
name='Person',
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')),
('f_name', models.CharField(help_text='The persons first name', max_length=64, verbose_name='First Name')),
('m_name', models.CharField(blank=True, default=None, help_text='The persons middle name(s)', max_length=100, null=True, verbose_name='Middle Name(s)')),
('l_name', models.CharField(help_text='The persons Last name', max_length=64, verbose_name='Last Name')),
('dob', models.DateField(blank=True, default=None, help_text='The Persons Date of Birth (DOB)', null=True, verbose_name='DOB')),
],
options={
'verbose_name': 'Person',
'verbose_name_plural': 'People',
'ordering': ['l_name', 'm_name', 'f_name', 'dob'],
},
bases=('access.entity',),
),
migrations.CreateModel(
name='EntityHistory',
fields=[
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.entity', verbose_name='Model')),
],
options={
'verbose_name': 'Entity History',
'verbose_name_plural': 'Entity History',
'db_table': 'access_entity_history',
'ordering': ['-created'],
},
bases=('core.modelhistory',),
),
migrations.CreateModel(
name='EntityNotes',
fields=[
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.entity', verbose_name='Model')),
],
options={
'verbose_name': 'Entity Note',
'verbose_name_plural': 'Entity Notes',
'db_table': 'access_entity_notes',
'ordering': ['-created'],
},
bases=('core.modelnotes',),
),
migrations.CreateModel(
name='Role',
fields=[
('model_notes', models.TextField(blank=True, default=None, help_text='Tid bits of information', null=True, verbose_name='Notes')),
('id', models.AutoField(help_text='Primary key of the entry', primary_key=True, serialize=False, unique=True, verbose_name='ID')),
('name', models.CharField(help_text='Name of this role', max_length=30, verbose_name='Name')),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of creation', verbose_name='Created')),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of last modification', verbose_name='Modified')),
('organization', models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='+', to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Organization')),
('permissions', models.ManyToManyField(blank=True, help_text='Permissions part of this role', related_name='roles', to='auth.permission', verbose_name='Permissions')),
],
options={
'verbose_name': 'Role',
'verbose_name_plural': 'Roles',
'ordering': ['organization', 'name'],
'unique_together': {('organization', 'name')},
},
),
migrations.CreateModel(
name='RoleHistory',
fields=[
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.role', verbose_name='Model')),
],
options={
'verbose_name': 'Role History',
'verbose_name_plural': 'Role History',
'db_table': 'access_role_history',
'ordering': ['-created'],
},
bases=('core.modelhistory',),
),
migrations.CreateModel(
name='RoleNotes',
fields=[
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.role', verbose_name='Model')),
],
options={
'verbose_name': 'Role Note',
'verbose_name_plural': 'Role Notes',
'db_table': 'access_role_notes',
'ordering': ['-created'],
},
bases=('core.modelnotes',),
),
migrations.CreateModel(
name='Contact',
fields=[
('person_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.person')),
('directory', models.BooleanField(blank=True, default=True, help_text='Show contact details in directory', verbose_name='Show in Directory')),
('email', models.EmailField(help_text='E-mail address for this person', max_length=254, unique=True, verbose_name='E-Mail')),
],
options={
'verbose_name': 'Contact',
'verbose_name_plural': 'Contacts',
'ordering': ['email'],
},
bases=('access.person',),
),
]

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

@ -4,7 +4,9 @@ from django.contrib.auth.models import Group
from django.core.exceptions import PermissionDenied
from django.utils.functional import cached_property
from .models import Organization, Team
from access.models.tenant import Tenant as Organization
from access.models.team import Team
class OrganizationMixin():
@ -258,7 +260,7 @@ class OrganizationMixin():
self.permission_required = permissions_required
organization_manager_models = [
'access.organization',
'access.tenant',
'access.team',
'access.teamusers',
]
@ -324,7 +326,7 @@ class OrganizationMixin():
if required_permission.replace(
'view_', ''
) == 'access.organization' and len(self.kwargs) == 0:
) == 'access.tenant' and len(self.kwargs) == 0:
return True

View File

@ -1,5 +1,13 @@
from django.contrib.auth.models import User, Group
from access.models import Organization, Team
import django
from django.contrib.auth.models import Group
from django.db import models
from access.models.tenant import Tenant as Organization
from access.models.team import Team
User = django.contrib.auth.get_user_model()
class OrganizationMixin:
@ -85,7 +93,7 @@ class OrganizationMixin:
self._obj_organization = obj.organization
elif str(self.model._meta.verbose_name).lower() == 'organization':
elif str(self.model._meta.verbose_name).lower() == 'tenant':
self._obj_organization = obj
@ -126,7 +134,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])
@ -253,7 +261,7 @@ class OrganizationMixin:
parent_model: str = None
parent_model: models.Model = None
""" Parent Model
This attribute defines the parent model for the model in question. The parent model when defined

View File

@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
from rest_framework import exceptions
from rest_framework.permissions import DjangoObjectPermissions
from access.models import TenancyObject
from access.models.tenancy import Tenant, TenancyObject
from core import exceptions as centurion_exceptions
@ -14,10 +14,10 @@ from core import exceptions as centurion_exceptions
class OrganizationPermissionMixin(
DjangoObjectPermissions,
):
"""Organization Permission Mixin
"""Tenant Permission Mixin
This class is to be used as the permission class for API `Views`/`ViewSets`.
In combination with the `OrganizationPermissionsMixin`, permission checking
In combination with the `TenantPermissionsMixin`, permission checking
will be done to ensure the user has the correct permissions to perform the
CRUD operation.
@ -116,15 +116,27 @@ class OrganizationPermissionMixin(
try:
if (
view.model.__name__ == 'UserSettings'
and request._user.id == int(view.kwargs.get('pk', 0))
(
view.model.__name__ == 'UserSettings'
and request._user.id == int(view.kwargs.get('pk', 0))
)
or (
view.model.__name__ == 'AuthToken'
and request._user.id == int(view.kwargs.get('model_id', 0))
)
):
return True
elif (
view.model.__name__ == 'UserSettings'
and request._user.id != int(view.kwargs.get('pk', 0))
(
view.model.__name__ == 'UserSettings'
and request._user.id != int(view.kwargs.get('pk', 0))
)
or (
view.model.__name__ == 'AuthToken'
and request._user.id != int(view.kwargs.get('model_id', 0))
)
):
@ -154,7 +166,7 @@ class OrganizationPermissionMixin(
raise centurion_exceptions.PermissionDenied()
obj_organization: Organization = view.get_obj_organization(
obj_organization: Tenant = view.get_obj_organization(
request = request
)
@ -271,8 +283,14 @@ class OrganizationPermissionMixin(
if (
view.model.__name__ == 'UserSettings'
and request._user.id == int(view.kwargs.get('pk', 0))
(
view.model.__name__ == 'UserSettings'
and request._user.id == int(view.kwargs.get('pk', 0))
)
or (
view.model.__name__ == 'AuthToken'
and request._user.id == int(view.kwargs.get('model_id', 0))
)
):
return True

View File

@ -1,594 +0,0 @@
from django.conf import settings
from django.db import models
from django.contrib.auth.models import User, Group, Permission
from rest_framework.reverse import reverse
from .fields import *
from core import exceptions as centurion_exceptions
from core.middleware.get_request import get_request
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})
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
This function filters the data fetched from the database to that which is from the organizations
the user is a part of.
!!! danger "Requirement"
This method may be overridden however must still be called from the overriding function. i.e. `super().get_queryset()`
## Workflow
This functions workflow is as follows:
- Fetch the user from the request
- Check if the user is authenticated
- Iterate over the users teams
- Store unique organizations from users teams
- return results
Returns:
(queryset): **super user**: return unfiltered data.
(queryset): **not super user**: return data from the stored unique organizations.
"""
request = get_request()
user_organizations: list(str()) = []
if request:
if request.app_settings.global_organization:
user_organizations += [ request.app_settings.global_organization.id ]
user = request.user
if user.is_authenticated:
for team in request.tenancy._user_teams:
if team.organization.id not in user_organizations:
if not 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:
return super().get_queryset().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()
class TenancyObject(SaveHistory):
""" Tenancy Model Abstrct class.
This class is for inclusion wihtin **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 = TenancyManager()
""" Multi-Tenanant Objects """
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('You must provide an organization')
id = models.AutoField(
blank=False,
help_text = 'ID of the item',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
organization = models.ForeignKey(
Organization,
blank = False,
help_text = 'Organization this belongs to',
null = False,
on_delete = models.CASCADE,
validators = [validatate_organization_exists],
verbose_name = 'Organization'
)
is_global = models.BooleanField(
blank = False,
default = False,
help_text = 'Is this a global object?',
verbose_name = 'Global Object'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null = True,
verbose_name = 'Notes',
)
def get_organization(self) -> Organization:
return self.organization
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_name = str(self._meta.verbose_name.lower()).replace(' ', '_')
if request:
return reverse(f"v2:_api_v2_{model_name}-detail", request=request, kwargs = self.get_url_kwargs() )
return reverse(f"v2:_api_v2_{model_name}-detail", kwargs = self.get_url_kwargs() )
def get_url_kwargs(self) -> dict:
"""Fetch the URL kwargs
Returns:
dict: kwargs required for generating the URL with `reverse`
"""
return {
'pk': self.id
}
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.clean()
if not getattr(self, 'organization', None):
raise centurion_exceptions.ValidationError(
detail = {
'organization': 'Organization is required'
},
code = 'required'
)
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
class Team(Group, TenancyObject):
class Meta:
ordering = [ 'team_name' ]
verbose_name = 'Team'
verbose_name_plural = "Teams"
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
team_name = models.CharField(
blank = False,
help_text = 'Name to give this team',
max_length = 50,
unique = False,
verbose_name = 'Name',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
page_layout: dict = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'team_name',
'created',
'modified',
],
"right": [
'model_notes',
]
},
{
"layout": "table",
"name": "Users",
"field": "users",
},
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
},
]
table_fields: list = [
'team_name',
'modified',
'created',
]
def get_url( self, request = None ) -> str:
model_name = str(self._meta.verbose_name.lower()).replace(' ', '_')
if request:
return reverse(f"v2:_api_v2_organization_team-detail", request=request, kwargs = self.get_url_kwargs() )
return reverse(f"v2:_api_v2_organization_team-detail", kwargs = self.get_url_kwargs() )
def get_url_kwargs(self) -> dict:
"""Fetch the URL kwargs
Returns:
dict: kwargs required for generating the URL with `reverse`
"""
return {
'organization_id': self.organization.id,
'pk': self.id
}
@property
def parent_object(self):
""" Fetch the parent object """
return self.organization
def permission_list(self) -> list:
permission_list = []
for permission in self.permissions.all():
if str(permission.content_type.app_label + '.' + permission.codename) in permission_list:
continue
permission_list += [ str(permission.content_type.app_label + '.' + permission.codename) ]
return [permission_list, self.permissions.all()]
def __str__(self):
return self.organization.name + ', ' + self.team_name
class TeamUsers(SaveHistory):
class Meta:
ordering = ['user']
verbose_name = "Team User"
verbose_name_plural = "Team Users"
id = models.AutoField(
blank=False,
help_text = 'ID of this Team User',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
team = models.ForeignKey(
Team,
blank = False,
help_text = 'Team user belongs to',
null = False,
on_delete=models.CASCADE,
related_name="team",
verbose_name = 'Team'
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank = False,
help_text = 'User who will be added to the team',
null = False,
on_delete=models.CASCADE,
verbose_name = 'User'
)
manager = models.BooleanField(
blank=True,
default=False,
help_text = 'Is this user to be a manager of this team',
verbose_name='manager',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
page_layout: list = []
table_fields: list = [
'user',
'manager'
]
def delete(self, using=None, keep_parents=False):
""" Delete Team
Overrides, post-action
As teams are an extension of Groups, remove the user to the team.
"""
super().delete(using=using, keep_parents=keep_parents)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.remove(group)
def get_organization(self) -> Organization:
return self.team.organization
def get_url( self, request = None ) -> str:
url_kwargs: dict = {
'organization_id': self.team.organization.id,
'team_id': self.team.id,
'pk': self.id
}
print(f'url kwargs are: {url_kwargs}')
if request:
return reverse(f"v2:_api_v2_organization_team_user-detail", request=request, kwargs = url_kwargs )
return reverse(f"v2:_api_v2_organization_team_user-detail", kwargs = url_kwargs )
def save(self, *args, **kwargs):
""" Save Team
Overrides, post-action
As teams are an extension of groups, add the user to the matching group.
"""
super().save(*args, **kwargs)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.add(group)
@property
def parent_object(self):
""" Fetch the parent object """
return self.team
def __str__(self):
return self.user.username

View File

@ -0,0 +1,4 @@
from . import contact
from . import company_base
from . import person
from . import role

View File

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

View File

@ -0,0 +1,119 @@
from django.db import models
from access.models.person import Person
class Contact(
Person
):
class Meta:
ordering = [
'email',
]
sub_model_type = 'contact'
verbose_name = 'Contact'
verbose_name_plural = 'Contacts'
directory = models.BooleanField(
blank = True,
default = True,
help_text = 'Show contact details in directory',
null = False,
verbose_name = 'Show in Directory',
)
email = models.EmailField(
blank = False,
help_text = 'E-mail address for this person',
unique = True,
verbose_name = 'E-Mail',
)
def __str__(self) -> str:
return self.f_name + ' ' + self.l_name
documentation = ''
history_model_name = 'contact'
page_layout: list = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'created',
'modified',
],
"right": [
'model_notes',
'directory',
]
},
{
"name": "Personal Details",
"layout": "double",
"left": [
'display_name',
'dob',
],
"right": [
'f_name',
'm_name',
'l_name',
]
},
{
"name": "",
"layout": "double",
"left": [
'email',
],
"right": [
'',
]
}
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
},
]
table_fields: list = [
{
"field": "display_name",
"type": "link",
"key": "_self"
},
'f_name',
'l_name',
'email',
'organization',
'created',
]

249
app/access/models/entity.py Normal file
View File

@ -0,0 +1,249 @@
from django.db import models
from rest_framework.reverse import reverse
from access.fields import AutoCreatedField, AutoLastModifiedField
from access.models.tenancy import TenancyObject
from core.lib.feature_not_used import FeatureNotUsed
class Entity(
TenancyObject
):
class Meta:
ordering = [
'created',
'modified',
'organization',
]
sub_model_type = 'entity'
verbose_name = '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,
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:
return f'{self.entity_type} {self.pk}'
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',
'display_name',
'created',
'modified',
]
def get_related_field_name(self) -> str:
meta = getattr(self, '_meta')
for related_object in getattr(meta, 'related_objects', []):
if not issubclass(related_object.related_model, Entity):
continue
if getattr(self, related_object.name, None):
if(
not str(related_object.name).endswith('history')
and not str(related_object.name).endswith('notes')
):
return related_object.name
break
return ''
def get_related_model(self):
"""Recursive model Fetch
Returns the lowest model found in a chain of inherited models.
Args:
model (models.Model, optional): Model to fetch the child model from. Defaults to None.
Returns:
models.Model: Lowset model found in inherited model chain
"""
related_model_name = self.get_related_field_name()
related_model = getattr(self, related_model_name, None)
if related_model_name == '':
related_model = None
elif related_model is None:
related_model = self
elif hasattr(related_model, 'get_related_field_name'):
if related_model.get_related_field_name() != '':
related_model = related_model.get_related_model()
return related_model
def get_url_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

@ -0,0 +1,55 @@
from django.db import models
from access.models.entity import Entity
from core.models.model_history import ModelHistory
from devops.models.feature_flag import FeatureFlag
class EntityHistory(
ModelHistory
):
class Meta:
db_table = 'access_entity_history'
ordering = ModelHistory._meta.ordering
verbose_name = 'Entity History'
verbose_name_plural = 'Entity History'
model = models.ForeignKey(
Entity,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'history',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_object(self):
return self
def get_serialized_model(self, serializer_context):
model = None
from access.serializers.entity import BaseSerializer
model = BaseSerializer(self.model, context = serializer_context)
return model

View File

@ -0,0 +1,45 @@
from django.db import models
from access.models.entity import Entity
from core.models.model_notes import ModelNotes
class EntityNotes(
ModelNotes
):
class Meta:
db_table = 'access_entity_notes'
ordering = ModelNotes._meta.ordering
verbose_name = 'Entity Note'
verbose_name_plural = 'Entity Notes'
model = models.ForeignKey(
Entity,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'notes',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_url_kwargs(self) -> dict:
return {
'model_id': self.model.pk,
'pk': self.pk
}

View File

@ -0,0 +1 @@
from .tenant import Tenant as Organization

View File

@ -0,0 +1,53 @@
from django.db import models
from core.models.model_history import ModelHistory
from access.models.tenant import Tenant
class OrganizationHistory(
ModelHistory
):
class Meta:
db_table = 'access_organization_history'
ordering = ModelHistory._meta.ordering
verbose_name = 'Organization History'
verbose_name_plural = 'Organization History'
model = models.ForeignKey(
Tenant,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'history',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_object(self):
return self
def get_serialized_model(self, serializer_context):
model = None
from access.serializers.organization import TenantBaseSerializer
model = TenantBaseSerializer(self.model, context = serializer_context)
return model

View File

@ -0,0 +1,45 @@
from django.db import models
from access.models.tenant import Tenant
from core.models.model_notes import ModelNotes
class OrganizationNotes(
ModelNotes
):
class Meta:
db_table = 'access_organization_notes'
ordering = ModelNotes._meta.ordering
verbose_name = 'Organization Note'
verbose_name_plural = 'Organization Notes'
model = models.ForeignKey(
Tenant,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'notes',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_url_kwargs(self) -> dict:
return {
'model_id': self.model.pk,
'pk': self.pk
}

119
app/access/models/person.py Normal file
View File

@ -0,0 +1,119 @@
from django.db import models
from core.exceptions import ValidationError
from access.models.entity import Entity
class Person(
Entity
):
class Meta:
ordering = [
'l_name',
'm_name',
'f_name',
'dob',
]
sub_model_type = 'person'
verbose_name = 'Person'
verbose_name_plural = 'People'
f_name = models.CharField(
blank = False,
help_text = 'The persons first name',
max_length = 64,
unique = False,
verbose_name = 'First Name'
)
m_name = models.CharField(
blank = True,
help_text = 'The persons middle name(s)',
max_length = 100,
null = True,
unique = False,
verbose_name = 'Middle Name(s)'
)
l_name = models.CharField(
blank = False,
help_text = 'The persons Last name',
max_length = 64,
unique = False,
verbose_name = 'Last Name'
)
dob = models.DateField(
blank = True,
help_text = 'The Persons Date of Birth (DOB)',
null = True,
unique = False,
verbose_name = 'DOB',
)
def __str__(self) -> str:
return self.f_name + ' ' + self.l_name + f' (DOB: {self.dob})'
documentation = ''
history_model_name = 'person'
page_layout: dict = []
table_fields: list = [
'organization',
'f_name',
'l_name',
'dob',
'created',
]
def clean(self):
super().clean()
if self.dob is not None:
if self.pk:
duplicate_entry = Person.objects.filter(
f_name = self.f_name,
l_name = self.l_name,
).exclude(
pk = self.pk
)
else:
duplicate_entry = Person.objects.filter(
f_name = self.f_name,
l_name = self.l_name,
)
for entry in duplicate_entry:
if(
entry.f_name == self.f_name
and entry.m_name == self.m_name
and entry.l_name == self.l_name
and entry.dob == self.dob
):
raise ValidationError(
detail = {
'dob': f'Person {self.f_name} {self.l_name} already exists with this birthday {entry.dob}'
},
code = 'duplicate_person_on_dob'
)

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

@ -0,0 +1,143 @@
from django.contrib.auth.models import Permission
from django.db import models
from access.fields import AutoCreatedField, AutoLastModifiedField
from access.models.tenancy import TenancyObject
class Role(
TenancyObject
):
class Meta:
ordering = [
'organization',
'name',
]
unique_together = [
'organization',
'name'
]
verbose_name = 'Role'
verbose_name_plural = 'Roles'
id = models.AutoField(
blank=False,
help_text = 'Primary key of the entry',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
name = models.CharField(
blank = False,
help_text = 'Name of this role',
max_length = 30,
unique = False,
verbose_name = 'Name'
)
permissions = models.ManyToManyField(
Permission,
blank = True,
help_text = 'Permissions part of this role',
related_name = 'roles',
symmetrical = False,
verbose_name = 'Permissions'
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
is_global = None
def __str__(self) -> str:
return str( self.organization ) + ' / ' + self.name
documentation = ''
page_layout: dict = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'name',
'created',
'modified',
],
"right": [
'model_notes',
]
},
{
"layout": "single",
"name": "Permissions",
"fields": [
"permissions",
]
},
]
},
{
"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 = [
'organization',
'name',
'created',
'modified',
]
def save_history(self, before: dict, after: dict) -> bool:
from access.models.role_history import RoleHistory
history = super().save_history(
before = before,
after = after,
history_model = RoleHistory
)
return history

View File

@ -0,0 +1,53 @@
from django.db import models
from core.models.model_history import ModelHistory
from access.models.role import Role
class RoleHistory(
ModelHistory
):
class Meta:
db_table = 'access_role_history'
ordering = ModelHistory._meta.ordering
verbose_name = 'Role History'
verbose_name_plural = 'Role History'
model = models.ForeignKey(
Role,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'history',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_object(self):
return self
def get_serialized_model(self, serializer_context):
model = None
from access.serializers.role import BaseSerializer
model = BaseSerializer(self.model, context = serializer_context)
return model

View File

@ -0,0 +1,45 @@
from django.db import models
from access.models.role import Role
from core.models.model_notes import ModelNotes
class RoleNotes(
ModelNotes
):
class Meta:
db_table = 'access_role_notes'
ordering = ModelNotes._meta.ordering
verbose_name = 'Role Note'
verbose_name_plural = 'Role Notes'
model = models.ForeignKey(
Role,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'notes',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_url_kwargs(self) -> dict:
return {
'model_id': self.model.pk,
'pk': self.pk
}

191
app/access/models/team.py Normal file
View File

@ -0,0 +1,191 @@
from django.db import models
from django.contrib.auth.models import Group
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField
)
from access.models.tenant import Tenant
from access.models.tenancy import TenancyObject
from core import exceptions as centurion_exceptions
class Team(Group, TenancyObject):
class Meta:
ordering = [ 'team_name' ]
verbose_name = 'Team'
verbose_name_plural = "Teams"
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if self.organization_id:
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
def validatate_organization_exists(self):
"""Ensure that the user did provide an organization
Raises:
ValidationError: User failed to supply organization.
"""
if not self:
raise centurion_exceptions.ValidationError('You must provide an organization')
team_name = models.CharField(
blank = False,
help_text = 'Name to give this team',
max_length = 50,
unique = False,
verbose_name = 'Name',
)
organization = models.ForeignKey(
Tenant,
blank = False,
help_text = 'Tenant this belongs to',
null = False,
on_delete = models.CASCADE,
validators = [validatate_organization_exists],
verbose_name = 'Tenant'
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
page_layout: dict = [
{
"name": "Details",
"slug": "details",
"sections": [
{
"layout": "double",
"left": [
'organization',
'team_name',
'created',
'modified',
],
"right": [
'model_notes',
]
},
{
"layout": "table",
"name": "Users",
"field": "users",
},
]
},
{
"name": "Knowledge Base",
"slug": "kb_articles",
"sections": [
{
"layout": "table",
"field": "knowledge_base",
}
]
},
{
"name": "Notes",
"slug": "notes",
"sections": []
},
]
table_fields: list = [
'team_name',
'modified',
'created',
]
def get_url( self, request = None ) -> str:
if request:
return reverse(f"v2:_api_v2_organization_team-detail", request=request, kwargs = self.get_url_kwargs() )
return reverse(f"v2:_api_v2_organization_team-detail", kwargs = self.get_url_kwargs() )
def get_url_kwargs(self) -> dict:
"""Fetch the URL kwargs
Returns:
dict: kwargs required for generating the URL with `reverse`
"""
return {
'organization_id': self.organization.id,
'pk': self.id
}
def get_url_kwargs_notes(self) -> dict:
"""Fetch the URL kwargs for model notes
Returns:
dict: notes kwargs required for generating the URL with `reverse`
"""
return {
'organization_id': self.organization.id,
'model_id': self.id
}
# @property
# def parent_object(self):
# """ Fetch the parent object """
# return self.organization
def permission_list(self) -> list:
permission_list = []
for permission in self.permissions.all():
if str(permission.content_type.app_label + '.' + permission.codename) in permission_list:
continue
permission_list += [ str(permission.content_type.app_label + '.' + permission.codename) ]
return [permission_list, self.permissions.all()]
def __str__(self):
return self.organization.name + ', ' + self.team_name
def save_history(self, before: dict, after: dict) -> bool:
from access.models.team_history import TeamHistory
history = super().save_history(
before = before,
after = after,
history_model = TeamHistory
)
return history

View File

@ -0,0 +1,53 @@
from django.db import models
from core.models.model_history import ModelHistory
from access.models.team import Team
class TeamHistory(
ModelHistory
):
class Meta:
db_table = 'access_team_history'
ordering = ModelHistory._meta.ordering
verbose_name = 'Team History'
verbose_name_plural = 'Team History'
model = models.ForeignKey(
Team,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'history',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_object(self):
return self
def get_serialized_model(self, serializer_context):
model = None
from access.serializers.teams import TeamBaseSerializer
model = TeamBaseSerializer(self.model, context = serializer_context)
return model

View File

@ -0,0 +1,54 @@
from django.db import models
from rest_framework.reverse import reverse
from access.models.team import Team
from core.models.model_notes import ModelNotes
class TeamNotes(
ModelNotes
):
class Meta:
db_table = 'access_team_notes'
ordering = ModelNotes._meta.ordering
verbose_name = 'Team Note'
verbose_name_plural = 'Team Notes'
model = models.ForeignKey(
Team,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'notes',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_url( self, request = None ) -> str:
kwargs = {
'organization_id': self.organization.pk,
'model_id': self.model.pk,
'pk': self.pk
}
if request:
return reverse("v2:_api_v2_team_note-detail", request=request, kwargs = kwargs )
return reverse("v2:_api_v2_team_note-detail", kwargs = kwargs )

View File

@ -0,0 +1,152 @@
import django
from django.conf import settings
from django.db import models
from django.contrib.auth.models import Group
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField
)
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
User = django.contrib.auth.get_user_model()
class TeamUsers(SaveHistory):
class Meta:
ordering = ['user']
verbose_name = "Team User"
verbose_name_plural = "Team Users"
id = models.AutoField(
blank=False,
help_text = 'ID of this Team User',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
team = models.ForeignKey(
Team,
blank = False,
help_text = 'Team user belongs to',
null = False,
on_delete=models.CASCADE,
related_name="team",
verbose_name = 'Team'
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank = False,
help_text = 'User who will be added to the team',
null = False,
on_delete=models.CASCADE,
verbose_name = 'User'
)
manager = models.BooleanField(
blank=True,
default=False,
help_text = 'Is this user to be a manager of this team',
verbose_name='manager',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
page_layout: list = []
table_fields: list = [
'user',
'manager'
]
history_app_label: str = None
history_model_name: str = None
kb_model_name: str = None
note_basename: str = None
def delete(self, using=None, keep_parents=False):
""" Delete Team
Overrides, post-action
As teams are an extension of Groups, remove the user to the team.
"""
super().delete(using=using, keep_parents=keep_parents)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.remove(group)
def get_organization(self) -> Tenant:
return self.team.organization
def get_url( self, request = None ) -> str:
url_kwargs: dict = {
'organization_id': self.team.organization.id,
'team_id': self.team.id,
'pk': self.id
}
print(f'url kwargs are: {url_kwargs}')
if request:
return reverse(f"v2:_api_v2_organization_team_user-detail", request=request, kwargs = url_kwargs )
return reverse(f"v2:_api_v2_organization_team_user-detail", kwargs = url_kwargs )
def get_url_kwargs_notes(self):
return FeatureNotUsed
def save(self, *args, **kwargs):
""" Save Team
Overrides, post-action
As teams are an extension of groups, add the user to the matching group.
"""
super().save(*args, **kwargs)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.add(group)
@property
def parent_object(self):
""" Fetch the parent object """
return self.team
def __str__(self):
return self.user.username

View File

@ -0,0 +1,299 @@
import django
import logging
from django.db import models
from rest_framework.reverse import reverse
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
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
This function filters the data fetched from the database to that which is from the organizations
the user is a part of.
!!! danger "Requirement"
This method may be overridden however must still be called from the overriding function. i.e. `super().get_queryset()`
## Workflow
This functions workflow is as follows:
- Fetch the user from the request
- Check if the user is authenticated
- Iterate over the users teams
- Store unique organizations from users teams
- return results
Returns:
(queryset): **super user**: return unfiltered data.
(queryset): **not super user**: return data from the stored unique organizations.
"""
request = get_request()
user_organizations: list(str()) = []
if request:
if request.app_settings.global_organization:
user_organizations += [ request.app_settings.global_organization.id ]
user = request.user
if user.is_authenticated:
for team in request.tenancy._user_teams:
if team.organization.id not in user_organizations:
if not 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:
return super().get_queryset().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()
class TenancyObject(SaveHistory):
""" Tenancy Model Abstrct class.
This class is for inclusion wihtin **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 = TenancyManager()
""" Multi-Tenanant Objects """
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 centurion_exceptions.ValidationError('You must provide an organization')
id = models.AutoField(
blank=False,
help_text = 'ID of the item',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
organization = models.ForeignKey(
Tenant,
blank = False,
help_text = 'Tenancy this belongs to',
null = False,
on_delete = models.CASCADE,
related_name = '+',
validators = [validatate_organization_exists],
verbose_name = 'Tenant'
)
is_global = models.BooleanField(
blank = False,
default = False,
help_text = 'Is this a global object?',
verbose_name = 'Global Object'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null = True,
verbose_name = 'Notes',
)
def get_organization(self) -> Tenant:
return self.organization
app_namespace: str = None
"""Application namespace.
Specify the applications namespace i.e. `devops`, without including
the API version, i.e. `v2:devops`.
"""
history_app_label: str = None
"""History Model Application Label
This value is derived from `<model>._meta.app_label`. This value should
only be used when there is model inheritence.
"""
history_model_name: str = None
"""History Model Model Name
This value is derived from `<model>._meta.model_name`. This value should
only be used when there is model inheritence.
"""
kb_model_name: str = None
"""Model name to use for KB article linking
This value is derived from `<model>._meta.model_name`. This value should
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
"""URL BaseName for the notes endpoint.
Don't specify the `app_namespace`, use property `app_namespace` above.
"""
def get_page_layout(self):
""" FEtch the page layout"""
return self.page_layout
def get_app_namespace(self) -> str:
"""Fetch the Application namespace if specified.
Returns:
str: Application namespace suffixed with colin `:`
None: No application namespace found.
"""
app_namespace = ''
if self.app_namespace:
app_namespace = self.app_namespace + ':'
return str(app_namespace)
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_name = str(self._meta.verbose_name.lower()).replace(' ', '_')
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"v2:" + self.get_app_namespace() + f"_api_v2_{model_name}-detail", kwargs = self.get_url_kwargs() )
def get_url_kwargs(self) -> dict:
"""Fetch the URL kwargs
Returns:
dict: kwargs required for generating the URL with `reverse`
"""
return {
'pk': self.id
}
def get_url_kwargs_notes(self) -> dict:
"""Fetch the URL kwargs for model notes
Returns:
dict: notes kwargs required for generating the URL with `reverse`
"""
return {
'model_id': self.id
}
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.clean()
if(
not getattr(self, 'organization', None)
and self._meta.model_name !='appsettingshistory' # App Settings for
):
raise centurion_exceptions.ValidationError(
detail = {
'organization': 'Tenant is required'
},
code = 'required'
)
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)

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

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

View File

View File

@ -0,0 +1,90 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from access.models.entity import Entity
from api.serializers import common
from access.serializers.organization import TenantBaseSerializer
@extend_schema_serializer(component_name = 'EntityBaseBaseSerializer')
class BaseSerializer(serializers.ModelSerializer):
display_name = serializers.SerializerMethodField('get_display_name')
def get_display_name(self, item) -> str:
return str( item )
url = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> str:
return item.get_url( request = self.context['view'].request )
class Meta:
model = Entity
fields = [
'id',
'display_name',
'url',
]
read_only_fields = [
'id',
'display_name',
'url',
]
@extend_schema_serializer(component_name = 'EntityBaseModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Entity Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = Entity
fields = [
'id',
'organization',
'entity_type',
'display_name',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'entity_type',
'created',
'modified',
'_urls',
]
@extend_schema_serializer(component_name = 'EntityBaseViewSerializer')
class ViewSerializer(ModelSerializer):
"""Entity Base View Model"""
organization = TenantBaseSerializer(many=False, read_only=True)

View File

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

View File

@ -0,0 +1,75 @@
from drf_spectacular.utils import extend_schema_serializer
from access.models.contact import Contact
from access.serializers.entity_person import (
BaseSerializer as BaseBaseSerializer,
ModelSerializer as BaseModelSerializer,
)
from access.serializers.organization import TenantBaseSerializer
class BaseSerializer(
BaseBaseSerializer,
):
pass
@extend_schema_serializer(component_name = 'ContactEntityModelSerializer')
class ModelSerializer(
BaseSerializer,
BaseModelSerializer,
):
"""Contact Model
This model first inherits from Person then inherits from the Entity Base model.
"""
class Meta:
model = Contact
fields = [
'id',
'person_ptr_id',
'organization',
'entity_type',
'display_name',
'f_name',
'm_name',
'l_name',
'dob',
'email',
'directory',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'entity_type',
'created',
'modified',
'_urls',
]
@extend_schema_serializer(component_name = 'ContactEntityViewSerializer')
class ViewSerializer(
ModelSerializer,
):
"""Contact View Model
This model inherits from the Person model.
"""
organization = TenantBaseSerializer(many=False, read_only=True)

View File

@ -0,0 +1,41 @@
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

@ -0,0 +1,73 @@
from drf_spectacular.utils import extend_schema_serializer
from access.models.person import Person
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 = 'PersonEntityModelSerializer')
class ModelSerializer(
BaseSerializer,
BaseModelSerializer,
):
"""Person Model
This model inherits from the Entity base model.
"""
class Meta:
model = Person
fields = [
'id',
'entity_ptr_id',
'organization',
'entity_type',
'display_name',
'f_name',
'm_name',
'l_name',
'dob',
'model_notes',
'is_global',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'entity_type',
'created',
'modified',
'_urls',
]
@extend_schema_serializer(component_name = 'PersonEntityViewSerializer')
class ViewSerializer(
ModelSerializer,
):
"""Person View Model
This model inherits from the Entity base model.
"""
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 import Organization
from access.models.tenant import Tenant
from app.serializers.user import UserBaseSerializer
from core import fields as centurion_field
Organization = Tenant
class OrganizationBaseSerializer(serializers.ModelSerializer):
class TenantBaseSerializer(serializers.ModelSerializer):
display_name = serializers.SerializerMethodField('get_display_name')
@ -24,7 +25,7 @@ class OrganizationBaseSerializer(serializers.ModelSerializer):
class Meta:
model = Organization
model = Tenant
fields = [
'id',
@ -42,8 +43,8 @@ class OrganizationBaseSerializer(serializers.ModelSerializer):
class OrganizationModelSerializer(
OrganizationBaseSerializer
class TenantModelSerializer(
TenantBaseSerializer
):
_urls = serializers.SerializerMethodField('get_url')
@ -60,6 +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
}
),
'teams': reverse("v2:_api_v2_organization_team-list", request=self._context['view'].request, kwargs={'organization_id': item.pk}),
}
@ -67,7 +75,7 @@ class OrganizationModelSerializer(
class Meta:
model = Organization
model = Tenant
fields = '__all__'
@ -91,7 +99,7 @@ class OrganizationModelSerializer(
]
class OrganizationViewSerializer(OrganizationModelSerializer):
class TenantViewSerializer(TenantModelSerializer):
pass
manager = UserBaseSerializer(many=False, read_only = True)

View File

@ -0,0 +1,48 @@
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

@ -0,0 +1,114 @@
from rest_framework import serializers
from rest_framework.reverse import reverse
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 TenantBaseSerializer
from api.serializers import common
from app.serializers.permission import PermissionBaseSerializer
@extend_schema_serializer(component_name = 'RoleBaseSerializer')
class BaseSerializer(serializers.ModelSerializer):
display_name = serializers.SerializerMethodField('get_display_name')
def get_display_name(self, item) -> str:
return str( item )
url = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> str:
return item.get_url( request = self.context['view'].request )
class Meta:
model = Role
fields = [
'id',
'display_name',
'url',
]
read_only_fields = [
'id',
'display_name',
'url',
]
@extend_schema_serializer(component_name = 'RoleModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Role Base Model"""
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
get_url = super().get_url( item = item )
get_url.update({
'tickets': reverse(
"v2:_api_v2_item_tickets-list",
request=self._context['view'].request,
kwargs={
'item_class': self.Meta.model._meta.model_name,
'item_id': item.pk
}
)
})
return get_url
permissions = serializers.PrimaryKeyRelatedField(many = True, queryset=permission_queryset(), required = False)
class Meta:
model = Role
fields = [
'id',
'organization',
'display_name',
'name',
'permissions',
'model_notes',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'created',
'modified',
'_urls',
]
@extend_schema_serializer(component_name = 'RoleViewSerializer')
class ViewSerializer(ModelSerializer):
"""Role Base View Model"""
organization = TenantBaseSerializer( many=False, read_only=True )
permissions = PermissionBaseSerializer( many=True, read_only=True )

View File

@ -0,0 +1,48 @@
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

@ -0,0 +1,48 @@
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

@ -2,7 +2,7 @@ from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models import TeamUsers
from access.models.team_user import TeamUsers
from api.serializers import common
@ -53,9 +53,13 @@ class TeamUserModelSerializer(
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request )
}
get_url = super().get_url( item = item )
del get_url['history']
del get_url['knowledge_base']
return get_url
class Meta:

View File

@ -2,12 +2,12 @@ from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models import Team
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
@ -61,16 +61,9 @@ class TeamModelSerializer(
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
'knowledge_base': reverse(
"v2:_api_v2_model_kb-list",
request=self._context['view'].request,
kwargs={
'model': self.Meta.model._meta.model_name,
'model_pk': item.pk
}
),
get_url = super().get_url( item = item )
get_url.update({
'users': reverse(
'v2:_api_v2_organization_team_user-list',
request=self.context['view'].request,
@ -79,7 +72,10 @@ class TeamModelSerializer(
'team_id': item.pk
}
)
}
})
return get_url
team_name = centurion_field.CharField( autolink = True )
@ -131,6 +127,6 @@ class TeamModelSerializer(
class TeamViewSerializer(TeamModelSerializer):
organization = OrganizationBaseSerializer(many=False, read_only=True)
organization = TenantBaseSerializer(many=False, read_only=True)
permissions = PermissionBaseSerializer(many = True)

View File

@ -1,7 +1,3 @@
import pytest
import unittest
from access.models import TenancyManager
@ -19,83 +15,18 @@ class TenancyObject:
"""
def test_history_save(self):
"""Confirm the desired intent for saving model history."""
# 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
def test_has_attr_get_organization(self):
""" TenancyObject attribute check
TenancyObject has function get_organization
"""
assert hasattr(self.model, 'get_organization')
def test_has_attr_is_global(self):
""" TenancyObject attribute check
TenancyObject has field is_global
"""
assert hasattr(self.model, 'is_global')
# assert self.model.save_model_history == self.should_model_history_be_saved
def test_has_attr_model_notes(self):
""" TenancyObject attribute check
# @pytest.mark.skip(reason="to be written")
# def test_edit_no_organization_fails(self):
# """ Devices must be assigned an organization
TenancyObject has field model_notes
"""
# Must not be able to edit an item without an organization
# """
# pass
assert hasattr(self.model, 'model_notes')
def test_has_attr_organization(self):
""" TenancyObject attribute check
TenancyObject has field organization
"""
assert hasattr(self.model, 'organization')
@pytest.mark.skip(reason="to be written")
def test_create_no_organization_fails(self):
""" Devices must be assigned an organization
Must not be able to create an item without an organization
"""
pass
@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
def test_has_attr_organization(self):
""" TenancyObject attribute check
TenancyObject has function objects
"""
assert hasattr(self.model, 'objects')
def test_attribute_is_type_objects(self):
""" Attribute Check
attribute `objects` must be set to `access.models.TenancyManager()`
"""
assert type(self.model.objects) is TenancyManager

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 = {
# 'entity_model': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
super().setUpTestData()
class CompanyMetadataTest(
CompanyMetadataTestCases,
TestCase,
):
pass

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

@ -0,0 +1,60 @@
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_v2_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class ContactPermissionsAPIPyTest(
ContactPermissionsAPITestCases,
):
pass

View File

@ -0,0 +1,46 @@
import pytest
from rest_framework.exceptions import ValidationError
from access.tests.functional.person.test_functional_person_serializer import (
MockView,
PersonSerializerInheritedCases
)
class ContactSerializerTestCases(
PersonSerializerInheritedCases
):
parameterized_test_data: dict = {
"email": {
'will_create': False,
'exception_key': 'required'
}
}
valid_data: dict = {
'email': 'contactentityduplicatetwo@unit.test',
}
"""Valid data used by serializer to create object"""
class ContactSerializerInheritedCases(
ContactSerializerTestCases,
):
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
class ContactSerializerPyTest(
ContactSerializerTestCases,
):
parameterized_test_data: dict = None

View File

@ -0,0 +1,58 @@
from django.test import TestCase
from access.models.contact import Contact
from access.tests.functional.person.test_functional_person_viewset import (
PersonViewSetInheritedCases
)
class ViewSetTestCases(
PersonViewSetInheritedCases,
):
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 ContactViewSetInheritedCases(
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 ContactViewSetTest(
ViewSetTestCases,
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

@ -0,0 +1,78 @@
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_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
super().setUpTestData()
class EntityMetadataTest(
EntityMetadataTestCases,
TestCase,
):
url_name = '_api_v2_entity'

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_v2_entity'
url_view_kwargs: dict = {}
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class EntityPermissionsAPIInheritedCases(
EntityPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
url_name = '_api_v2_entity_sub'
@pytest.fixture(scope='class')
def inherited_var_setup(self, request):
request.cls.url_kwargs.update({
'entity_model': self.model._meta.sub_model_type
})
request.cls.url_view_kwargs.update({
'entity_model': self.model._meta.sub_model_type
})
@pytest.fixture(scope='class', autouse = True)
def class_setup(self, request, django_db_blocker,
model,
var_setup,
prepare,
inherited_var_setup,
diff_org_model,
create_model,
):
pass
class EntityPermissionsAPIPyTest(
EntityPermissionsAPITestCases,
):
pass

View File

@ -0,0 +1,213 @@
import django
import pytest
from rest_framework.exceptions import ValidationError
User = django.contrib.auth.get_user_model()
class MockView:
_has_import: bool = False
"""User Permission
get_permission_required() sets this to `True` when user has import permission.
"""
_has_purge: bool = False
"""User Permission
get_permission_required() sets this to `True` when user has purge permission.
"""
_has_triage: bool = False
"""User Permission
get_permission_required() sets this to `True` when user has triage permission.
"""
class 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"""
@pytest.fixture( scope = 'class')
def setup_data(self,
request,
model,
django_db_blocker,
organization_one,
):
with django_db_blocker.unblock():
request.cls.organization = organization_one
valid_data = {}
for base in reversed(request.cls.__mro__):
if hasattr(base, 'valid_data'):
if base.valid_data is None:
continue
valid_data.update(**base.valid_data)
if len(valid_data) > 0:
request.cls.valid_data = valid_data
if 'organization' not in request.cls.valid_data:
request.cls.valid_data.update({
'organization': request.cls.organization.pk
})
request.cls.view_user = User.objects.create_user(username="cafs_test_user_view", password="password")
yield
with django_db_blocker.unblock():
request.cls.view_user.delete()
del request.cls.valid_data
@pytest.fixture( scope = 'class', autouse = True)
def class_setup(self,
setup_data,
):
pass
def test_serializer_valid_data(self, create_serializer):
"""Serializer Validation Check
Ensure that when creating an object with valid data, no validation
error occurs.
"""
view_set = MockView()
serializer = create_serializer(
context = {
'view': view_set,
},
data = self.valid_data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_valid_data_missing_field_is_valid(self, parameterized, param_key_test_data,
create_serializer,
param_value,
param_will_create,
):
"""Serializer Validation Check
Ensure that when creating an object with a user with import permission
and with valid data, no validation error occurs.
"""
valid_data = self.valid_data.copy()
del valid_data[param_value]
view_set = MockView()
view_set._has_import = True
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
is_valid = serializer.is_valid(raise_exception = False)
assert (
(
not param_will_create
and param_will_create == is_valid
)
or param_will_create == is_valid
)
class EntitySerializerInheritedCases(
EntitySerializerTestCases,
):
parameterized_test_data: dict = None
model = None
"""Model to test"""
valid_data: dict = None
"""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
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,
):
parameterized_test_data: dict = None

View File

@ -0,0 +1,266 @@
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 api.tests.abstract.api_serializer_viewset import SerializersTestCases
User = django.contrib.auth.get_user_model()
class ViewSetBase:
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 = {
'model_notes': 'added model note'
}
kwargs_create_item_diff_org: dict = {
'model_notes': 'added model note'
}
model = None
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = None
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
. create an organization that is different to item
2. Create a team
3. create teams with each permission: view, add, change, delete
4. create a user per team
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.different_organization = Organization.objects.create(name='test_different_organization')
self.view_user = User.objects.create_user(username="test_user_view", password="password")
self.item = self.model.objects.create(
organization = organization,
**self.kwargs_create_item
)
self.other_org_item = self.model.objects.create(
organization = self.different_organization,
**self.kwargs_create_item_diff_org
)
self.url_view_kwargs.update({ 'pk': self.item.id })
if self.add_data is not None:
self.add_data.update({
'organization': self.organization.id,
})
view_permissions = Permission.objects.get(
codename = 'view_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permissions])
add_permissions = Permission.objects.get(
codename = 'add_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
add_team = Team.objects.create(
team_name = 'add_team',
organization = organization,
)
add_team.permissions.set([add_permissions])
change_permissions = Permission.objects.get(
codename = 'change_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
change_team = Team.objects.create(
team_name = 'change_team',
organization = organization,
)
change_team.permissions.set([change_permissions])
delete_permissions = Permission.objects.get(
codename = 'delete_' + self.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = self.model._meta.app_label,
model = self.model._meta.model_name,
)
)
delete_team = Team.objects.create(
team_name = 'delete_team',
organization = organization,
)
delete_team.permissions.set([delete_permissions])
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.add_user = User.objects.create_user(username="test_user_add", password="password")
TeamUsers.objects.create(
team = add_team,
user = self.add_user
)
self.change_user = User.objects.create_user(username="test_user_change", password="password")
TeamUsers.objects.create(
team = change_team,
user = self.change_user
)
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
TeamUsers.objects.create(
team = delete_team,
user = self.delete_user
)
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
different_organization_team = Team.objects.create(
team_name = 'different_organization_team',
organization = self.different_organization,
)
different_organization_team.permissions.set([
view_permissions,
add_permissions,
change_permissions,
delete_permissions,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)
def test_sanity_is_asset_sub_model(self):
"""Sanity Test
This test ensures that the model being tested `self.model` is a
sub-model of `self.base_model`.
This test is required as the same viewset is used for all sub-models
of `Entity`
"""
assert issubclass(self.model, self.base_model)
class ViewSetTestCases(
ViewSetBase,
SerializersTestCases,
):
model = Entity
class EntityViewSetInheritedCases(
ViewSetTestCases,
):
model = None
url_name = '_api_v2_entity_sub'
@classmethod
def setUpTestData(self):
self.kwargs_create_item = {
**super().kwargs_create_item,
**self.kwargs_create_item
}
self.kwargs_create_item_diff_org = {
**super().kwargs_create_item_diff_org,
**self.kwargs_create_item_diff_org
}
self.url_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
self.url_view_kwargs = {
'entity_model': self.model._meta.sub_model_type
}
super().setUpTestData()
class EntityViewSetTest(
ViewSetTestCases,
TestCase,
):
url_name = '_api_v2_entity'

View File

@ -0,0 +1,81 @@
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

@ -0,0 +1,162 @@
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

@ -0,0 +1,32 @@
from django.test import TestCase
from access.models.organization_history import Tenant as 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,24 @@
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()
class OrganizationValidationAPI(
TestCase,
):
model = Organization
model = Tenant
@classmethod
def setUpTestData(self):
@ -45,7 +47,7 @@ class OrganizationValidationAPI(
Ensure that if creating and no name is provided a validation error occurs
"""
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = self.valid_data
)
@ -65,7 +67,7 @@ class OrganizationValidationAPI(
with pytest.raises(ValidationError) as err:
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = data
)
@ -85,7 +87,7 @@ class OrganizationValidationAPI(
del data['manager']
serializer = OrganizationModelSerializer(
serializer = TenantModelSerializer(
data = data
)

View File

@ -1,20 +1,25 @@
import django
import pytest
import unittest
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, 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 import Organization, Team, TeamUsers, Permission
from access.models.tenant import Tenant as Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
User = django.contrib.auth.get_user_model()
class ViewSetBase:
@ -313,4 +318,4 @@ class OrganizationMetadata(
menu_id = 'access'
menu_entry_id = 'organization'
menu_entry_id = 'tenant'

View File

@ -0,0 +1,116 @@
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

@ -0,0 +1,53 @@
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.tenant import Tenant as 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

@ -0,0 +1,65 @@
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 = {
# 'entity_model': self.model._meta.sub_model_type
# }
# self.url_view_kwargs = {
# 'entity_model': self.model._meta.sub_model_type
# }
super().setUpTestData()
class PersonMetadataTest(
PersonMetadataTestCases,
TestCase,
):
pass

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_v2_entity'
# url_view_kwargs: dict = {}
class PersonPermissionsAPIInheritedCases(
PersonPermissionsAPITestCases,
):
add_data: dict = None
kwargs_create_item: dict = None
kwargs_create_item_diff_org: dict = None
# url_name = '_api_v2_entity_sub'
# @pytest.fixture(scope='class')
# def inherited_var_setup(self, request):
# request.cls.url_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# request.cls.url_view_kwargs.update({
# 'entity_model': self.model._meta.sub_model_type
# })
# @pytest.fixture(scope='class', autouse = True)
# def class_setup(self, request, django_db_blocker,
# model,
# var_setup,
# prepare,
# inherited_var_setup,
# diff_org_model,
# create_model,
# ):
# pass
class PersonPermissionsAPIPyTest(
PersonPermissionsAPITestCases,
):
pass

View File

@ -0,0 +1,111 @@
import pytest
from rest_framework.exceptions import ValidationError
from access.tests.functional.entity.test_functional_entity_serializer import (
MockView,
EntitySerializerInheritedCases
)
class PersonSerializerTestCases(
EntitySerializerInheritedCases
):
parameterized_test_data: dict = {
"model_notes": {
'will_create': True,
},
"f_name": {
'will_create': False,
'exception_key': 'required'
},
"m_name": {
'will_create': True,
},
"l_name": {
'will_create': False,
'exception_key': 'required'
},
"dob": {
'will_create': True,
}
}
valid_data: dict = {
'f_name': 'Ian',
'm_name': 'Peter',
'l_name': 'Funny',
'dob': '2025-04-08',
}
"""Valid data used by serializer to create object"""
def test_serializer_validation_duplicate_f_name_l_name_dob(self, model, create_serializer):
"""Serializer Validation Check
Ensure that when creating with valid data and fields f_name, l_name and
dob already exists in the db a validation error occurs.
"""
valid_data = self.valid_data.copy()
valid_data['f_name'] = 'duplicate'
valid_data['organization'] = self.organization
obj = model.objects.create(
**valid_data
)
valid_data['organization'] = self.organization.id
if 'email' in valid_data: # Contact Entity
valid_data['email'] = 'abc@xyz.qwe'
if 'name' in valid_data: # Company Entity
valid_data['name'] = 'diff'
if 'employee_number' in valid_data: # Employee Entity
valid_data['employee_number'] = 13579
view_set = MockView()
with pytest.raises(ValidationError) as err:
serializer = create_serializer(
context = {
'view': view_set,
},
data = valid_data
)
serializer.is_valid(raise_exception = True)
serializer.save()
assert err.value.get_codes()['dob'] == 'duplicate_person_on_dob'
class PersonSerializerInheritedCases(
PersonSerializerTestCases,
):
parameterized_test_data: dict = None
valid_data: dict = None
"""Valid data used by serializer to create object"""
class PersonSerializerPyTest(
PersonSerializerTestCases,
):
parameterized_test_data: dict = None

View File

@ -0,0 +1,67 @@
from django.test import TestCase
from access.models.person import Person
from access.tests.functional.entity.test_functional_entity_viewset import (
EntityViewSetInheritedCases
)
class ViewSetTestCases(
EntityViewSetInheritedCases,
):
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 PersonViewSetInheritedCases(
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 PersonViewSetTest(
ViewSetTestCases,
TestCase,
):
pass

View File

@ -0,0 +1,55 @@
from django.test import TestCase
from access.models.role_history import Role, RoleHistory
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
class HistoryTestCases(
HistoryEntriesCommon,
):
history_model = RoleHistory
kwargs_create_obj: dict = {}
kwargs_delete_obj: dict = {}
model = Role
@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 RoleHistoryTest(
HistoryTestCases,
TestCase,
):
kwargs_create_obj: dict = {
'name': 'original_name'
}
kwargs_delete_obj: dict = {
'name': 'delete obj'
}

View File

@ -0,0 +1,83 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.models.tenant import Tenant as Organization
from access.serializers.role import Role, ModelSerializer
class ValidationSerializer(
TestCase,
):
model = Role
serializer = ModelSerializer
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an org
2. Create an item
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.diff_organization = Organization.objects.create(name='test_org_diff_org')
self.item = self.model.objects.create(
organization = self.organization,
name = 'one',
)
self.valid_data = {
'organization': self.organization.id,
'name': 'two',
'model_notes': 'dfsdfsd',
}
def test_serializer_validation_valid_data(self):
"""Serializer Validation Check
Ensure that if creating and no name is provided a validation error occurs
"""
serializer = self.serializer(
data = self.valid_data
)
assert serializer.is_valid( raise_exception = True )
def test_serializer_validation_no_name_exception(self):
"""Serializer Validation Check
Ensure that when creating and field name is not provided a
validation error occurs
"""
valid_data = self.valid_data.copy()
del valid_data['name']
with pytest.raises(ValidationError) as err:
serializer = self.serializer(
data = valid_data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['name'][0] == 'required'

View File

@ -0,0 +1,286 @@
import django
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.role import Role
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
from settings.models.app_settings import AppSettings
User = django.contrib.auth.get_user_model()
class ViewSetBase:
add_data: dict = None
app_namespace = 'v2'
change_data = { 'name': 'changed name' }
delete_data = {}
kwargs_create_item: dict = {}
kwargs_create_item_diff_org: dict = {}
kwargs_create_item_global_org_org: dict = {}
model = None
url_kwargs: dict = None
url_view_kwargs: dict = None
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.global_organization = Organization.objects.create(name='test_global_organization')
app_settings = AppSettings.objects.get(
owner_organization = None
)
app_settings.global_organization = self.global_organization
app_settings.save()
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
)
self.global_org_item = self.model.objects.create(
organization = self.global_organization,
model_notes = 'some more notes',
**self.kwargs_create_item_global_org_org
)
# self.url_kwargs = {'organization_id': self.organization.id}
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")
self.view_user = User.objects.create_user(username="test_user_view", 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
)
class RolePermissionsAPITest(
ViewSetBase,
APIPermissions,
TestCase,
):
add_data: dict = { 'name': 'added model note' }
kwargs_create_item: dict = { 'name': 'create item' }
kwargs_create_item_diff_org: dict = { 'name': 'diff org create' }
kwargs_create_item_global_org_org: dict = { 'name': 'global org create' }
model = Role
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_role'
class RoleViewSetTest(
ViewSetBase,
SerializersTestCases,
TestCase,
):
kwargs_create_item: dict = { 'name': 'create item' }
kwargs_create_item_diff_org: dict = { 'name': 'diff org create' }
kwargs_create_item_global_org_org: dict = { 'name': 'global org create' }
model = Role
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_role'
class RoleMetadataTest(
ViewSetBase,
MetadataAttributesFunctional,
TestCase,
):
kwargs_create_item: dict = { 'name': 'create item' }
kwargs_create_item_diff_org: dict = { 'name': 'diff org create' }
kwargs_create_item_global_org_org: dict = { 'name': 'global org create' }
model = Role
url_kwargs: dict = {}
url_view_kwargs: dict = {}
url_name = '_api_v2_role'

View File

@ -0,0 +1,37 @@
from django.test import TestCase
from access.models.team_history import Team, TeamHistory
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
class History(
HistoryEntriesCommon,
TestCase,
):
model = Team
history_model = TeamHistory
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.field_name = 'team_name'
self.obj = self.model.objects.create(
organization = self.organization,
# name = self.field_value_original,
team_name = self.field_value_original
)
self.obj_delete = self.model.objects.create(
organization = self.organization,
name = self.field_value_delete,
)
self.call_the_banners()

View File

@ -1,21 +1,27 @@
import django
import pytest
import unittest
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models import Organization, Team, TeamUsers, Permission
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:

View File

@ -1,13 +1,15 @@
import django
import pytest
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.middleware.request import Tenancy
from access.models import Organization, Permission
from access.models.tenant import Tenant as Organization
from access.serializers.teams import (
Team,
@ -16,6 +18,8 @@ from access.serializers.teams import (
from settings.models.app_settings import AppSettings
User = django.contrib.auth.get_user_model()
class MockView:

View File

@ -0,0 +1,127 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.viewsets.team_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_team_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,
name = 'note model'
),
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.different_organization,
name = 'note model'
),
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.viewset.model.model.field.related_model.objects.create(
organization = self.global_organization,
name = 'note model global_organization'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_kwargs = {
'organization_id': self.organization.id,
'model_id': self.item.model.pk,
}
self.url_view_kwargs = {
'organization_id': self.organization.id,
'model_id': self.item.model.pk,
'pk': self.item.id
}
class TeamModelNotesPermissionsAPI(
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 TeamModelNotesSerializer(
ViewSetBase,
ModelNotesSerializer,
TestCase,
):
pass
class TeamModelNotesMetadata(
ViewSetBase,
ModelNotesMetadata,
TestCase,
):
pass

View File

@ -0,0 +1,56 @@
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.team import Team
from access.models.team_notes import TeamNotes
class TeamNotesAPI(
ModelNotesNotesAPIFields,
TestCase,
):
model = TeamNotes
view_name: str = '_api_v2_team_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 = Team.objects.create(
organization = self.organization,
name = 'note model'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_view_kwargs = {
'organization_id': self.organization.pk,
'model_id': self.item.model.pk,
'pk': self.item.pk
}
self.make_request()

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