Compare commits

...

1055 Commits

Author SHA1 Message Date
Jon
ee31b9348f refactor(api): partial conversion to pytest for Common ViewSet Unit Test Suite
ref: #893 #876 #895
2025-07-26 09:30:36 +09:30
Jon
c252717bb7 chore(api): mark ALL ViewSet Unit Test Suites to skip
Viewset tests being refactored

ref: #893 #876 #895
2025-07-26 08:22:39 +09:30
Jon
4b7549b87b refactor(api): Rename create Serializer unit test to is_valid
ref: #893 #730
2025-07-26 07:37:29 +09:30
Jon
2806f6bc8e test(access): Add Serializer unit test suit for model Role
ref: #893 #894
2025-07-26 07:37:21 +09:30
Jon
b0b7208eaa test(access): Add Serializer unit test suit for model Person
ref: #893 #878
2025-07-26 07:34:25 +09:30
Jon
c089b6fdf4 test(access): Add Serializer unit test suit for model Entity
ref: #893 #877
2025-07-26 07:33:58 +09:30
Jon
b9a9346b3f test(access): Add Serializer unit test suit for model Contact
ref: #893 #879
2025-07-26 07:33:31 +09:30
Jon
d49f71d3fe test(access): Add Serializer unit test suit for model Company
ref: #893 #880
2025-07-26 07:32:34 +09:30
Jon
b63fd35c5b Merge pull request #892 from nofusscomputing/2025-07-25 2025-07-25 08:38:15 +09:30
Jon
14a3df16c8 chore(access): remove superfluous API Permissions Test suite for Tenant model
test already covered under meta api permissions

ref: #892
2025-07-25 08:13:46 +09:30
Jon
4dbef179c9 chore(core): refactor viewset checks so they work for model TicketBase
ref: #892 #889
2025-07-25 07:46:34 +09:30
Jon
18c1c4029e chore(accounting): refactor viewset checks so they work for model AssetBase
ref: #892 #887
2025-07-25 07:40:18 +09:30
Jon
52001959d9 chore(human_resources): refactor api perm checks so they work for model employee
ref: #892 #881
2025-07-25 07:39:46 +09:30
Jon
b78c0e3045 chore(access): refactor api perm checks so they work for model contact
ref: #892 #879
2025-07-25 07:39:17 +09:30
Jon
2e0953d0ed chore(access): refactor api perm checks so they work for model person
ref: #892 #878
2025-07-25 07:38:28 +09:30
Jon
bca0f0a3e2 test(itim): Refactor TicketSLM model API Fields render test Suite to PyTest
ref: #892 #890
2025-07-25 06:59:23 +09:30
Jon
bd51c55a15 test(itim): Refactor TicketRequest model API Fields render test Suite to PyTest
ref: #892 #891
2025-07-25 06:59:19 +09:30
Jon
bc0180b246 test(core): Refactor TicketBase model API Fields render test Suite to PyTest
ref: #892 #889
2025-07-25 06:59:05 +09:30
Jon
c7b16874d3 chore: correct fixture
ref: #892 #888
2025-07-25 06:39:26 +09:30
Jon
47daac3a1b Merge pull request #883 from nofusscomputing/2025-07-24 2025-07-24 19:15:40 +09:30
Jon
590535516b test(api): Refactor Test Suite for API Fields render tests to PyTest
ref: #883 #730 #876
2025-07-24 18:50:27 +09:30
Jon
1fbbbfba88 test(itam): Refactor ITAMAssetBase model API Fields render test Suite to PyTest
ref: #883 #888
2025-07-24 18:47:14 +09:30
Jon
767957ad66 test(accounting): Refactor AssetBase model API Fields render test Suite to PyTest
ref: #883 #887
2025-07-24 18:46:40 +09:30
Jon
82bbf88c6f test(core): Refactor TicketCommentSolution model API Fields render test Suite to PyTest
ref: #883 #886 #728
2025-07-24 18:23:53 +09:30
Jon
daf68103ad test(core): Refactor TicketCommentAction model API Fields render test Suite to PyTest
ref: #883 #885 #736
2025-07-24 18:21:50 +09:30
Jon
8159799afc test(core): Refactor TicketCommentBase model API Fields render test Suite to PyTest
ref: #883 #884 #726
2025-07-24 18:20:40 +09:30
Jon
25b8993462 test(human_resources): Refactor Employee model API Fields render test Suite to PyTest
ref: #883 #881
2025-07-24 17:13:52 +09:30
Jon
3fc2b538a8 test(access): Refactor Person model API Fields render test Suite to PyTest
ref: #883 #878
2025-07-24 17:13:07 +09:30
Jon
ca938d2b61 test(access): Refactor Entity model API Fields render test Suite to PyTest
ref: #883 #877
2025-07-24 17:12:35 +09:30
Jon
6e65802932 test(access): Refactor Contact model API Fields render test Suite to PyTest
ref: #883 #879
2025-07-24 17:11:35 +09:30
Jon
cbaaefa87d test(access): Refactor Company model API Fields render test Suite to PyTest
ref: #883 #880
2025-07-24 17:10:18 +09:30
Jon
6b28dbf346 fix(base): on fixture cleanup, only clean if obj exists
ref: #883 #730
2025-07-24 16:53:04 +09:30
Jon
a84676c227 refactor(base): normalize empty/not used to be models.NOT_PROVIDED
ref: #883 #730
2025-07-24 15:46:48 +09:30
Jon
8191785134 test(devops): Adjust functional model test to use fixture kwargs
ref: #883 #882
2025-07-24 15:37:38 +09:30
Jon
6e1d0c747d refactor(base): adjust functional model test to use fixture kwargs
ref: #883 #735
2025-07-24 15:36:23 +09:30
Jon
482014004c Merge pull request #873 from nofusscomputing/2025-07-23 2025-07-23 12:28:24 +09:30
Jon
3d45e0ddfc feat(access): Filter history permissions
ref: #873 #875 closes #643
2025-07-23 12:03:05 +09:30
Jon
36303ec28f chore(api): clean up/remove api v1 from docs
ref: #873 closes #343
2025-07-23 10:12:12 +09:30
Jon
5eb0931495 chore(api): re-enable notes/history api field check
ref: #873 closes #768
2025-07-23 09:58:48 +09:30
Jon
7392c9700d fix(core): required field must be null for logical chek to function
ref: #873 #768
2025-07-23 09:50:00 +09:30
Jon
68084d8a05 test: Ensure when obj created via serializer calls full_clean
ref: #873 #874 closes #445
2025-07-23 08:33:54 +09:30
Jon
76bf78d3f5 test: Ensure Clean methods called
ref: #873 #445
2025-07-23 08:21:06 +09:30
Jon
a749862dbc chore(access): correct pytest mark for model role
ref: #873
2025-07-23 07:29:33 +09:30
Jon
25d3c5cb5a chore(base): remove ui templates
ref: #873 closes #757
2025-07-23 07:06:32 +09:30
Jon
7c7bfaf26a chore(base): remove ui scripts
ref: #873 #757
2025-07-23 07:06:18 +09:30
Jon
39c09f066d chore(base): remove ui icons
ref: #873 #757
2025-07-23 07:06:07 +09:30
Jon
bc7344c757 chore(base): remove ui Stylesheets
ref: #873 #757
2025-07-23 07:03:04 +09:30
Jon
971e1d61ea Merge pull request #872 from nofusscomputing/2025-07-21 2025-07-23 06:35:17 +09:30
Jon
f6c1b77c43 test: Test case for model field type
ref: #872
2025-07-21 16:05:40 +09:30
Jon
c8fade9e4d refactor(api): Update Test Suite for AuthToken model
ref: #872 #869
2025-07-21 13:43:08 +09:30
Jon
31a03b4226 Merge pull request #871 from nofusscomputing/test-refactor-for-new-inheritance 2025-07-20 12:57:47 +09:30
Jon
de87364b9c refactor(tests): Unskip tests that'll work now due to model inheritance change
ref: #871 #771 #395 #859
2025-07-20 12:34:15 +09:30
Jon
a382561245 refactor(api): Update Test Suite for AuthToken model
ref: #871 closes #869 #735 #771
2025-07-20 12:13:53 +09:30
Jon
0b9eeea12c refactor(api): Update URL route name for Role AuthToken
ref: #870 #869
2025-07-20 12:01:30 +09:30
Jon
7800b1abb0 refactor(api): Switch to inherit from Centurion model for model AuthToken
ref: #871 #869
2025-07-20 12:00:02 +09:30
Jon
049cf6577e fix(itam): field slug no longer avail, use str
ref: #871 #820
2025-07-20 11:41:56 +09:30
Jon
c154cef6d5 refactor(access): When adding model role via api, status is 201/created
ref: #871 #844
2025-07-20 11:24:52 +09:30
Jon
132765833e refactor(itim): Update Test Suite for TicketCommentSolution model
ref: #871 closes #866
2025-07-20 10:59:03 +09:30
Jon
842fc38a98 chore(core): addd rand int to username
ref: #871
2025-07-20 07:24:07 +09:30
Jon
35d2e869b0 chore(core): dont delete obj on fixture clean
ref: #871
2025-07-20 07:09:16 +09:30
Jon
f0fdf4ed15 refactor(itim): Update Test Suite for TicketSLM model
ref: #871 closes #868
2025-07-18 19:34:32 +09:30
Jon
41576f9862 refactor(itim): Update Test Suite for TicketRequest model
ref: #871 closes #867
2025-07-18 19:34:17 +09:30
Jon
4ffbf81b71 refactor(core): Update Test Suite for TicketBase model
ref: #871 closes #861
2025-07-18 19:33:53 +09:30
Jon
cf06783aec refactor(core): Update Test Suite for TicketCommentSolution model
ref: #871 #866
2025-07-18 17:35:47 +09:30
Jon
80ab996851 refactor(core): Update Test Suite for TicketCommentAction model
ref: #871 #864
2025-07-18 17:35:00 +09:30
Jon
13cf3f8152 refactor(core): Update Test Suite for TicketCommentBase model
ref: #871 #865
2025-07-18 17:32:40 +09:30
Jon
9d78d112ec refactor(core): Initial Update Test Suite for TicketCommentBase model
ref: #871 #865
2025-07-17 00:12:13 +09:30
Jon
27e842a57f refactor(core): Update Tests to cater for inheritence changes
ref: #871 #861
2025-07-16 21:56:12 +09:30
Jon
8cc8fdad8b refactor(itim): Update Test Suite for RequestTicket model
ref: #871 #868
2025-07-16 21:29:52 +09:30
Jon
35c5c7ca66 refactor(itim): Update Test Suite for SLMTicket model
ref: #871 #868
2025-07-16 21:29:02 +09:30
Jon
645baa24d9 refactor(itim): Update Test Suite for SLMTicket model
ref: #871 #868
2025-07-15 21:51:05 +09:30
Jon
739cefbf1c refactor(core): Update Test Suite for TicketBase model
ref: #871 #863
2025-07-15 21:11:40 +09:30
Jon
15cf7096f2 chore(core): TicketBase model fn cleanup
ref: #871 #863
2025-07-15 21:11:13 +09:30
Jon
fd5b223632 chore: Update DB test fixture
ref: #871 #863
2025-07-15 21:06:50 +09:30
Jon
42ea8121a8 Merge pull request #870 from nofusscomputing/refactor-ticket-inheritence 2025-07-15 02:50:19 +09:30
Jon
d3741e74e2 refactor(core): Update Test Suite for TicketBase model
ref: #870 #861
2025-07-15 02:15:28 +09:30
Jon
b4f61223d6 refactor(core): Switch to inherit from Centurion model for model TicketBase
ref: #870 #863
2025-07-15 02:14:34 +09:30
Jon
1efa932fb9 refactor(core): Switch to inherit from Centurion model for model SLMTicketBase
ref: #870 #868
2025-07-14 22:20:04 +09:30
Jon
a04c963431 chore: Update DB test fixture
ref: #870 #816 #865
2025-07-13 23:20:09 +09:30
Jon
815075174a refactor(core): Update URL route name for Role TicketCommentBase
ref: #870 #865
2025-07-13 23:07:05 +09:30
Jon
9df5a5898e refactor(core): Switch to inherit from Centurion model for model TicketCommentBase
ref: #870 #865
2025-07-13 23:06:35 +09:30
Jon
ceabd56e1b refactor(core): Update URL route name for Role TicketBase
ref: #870 #861
2025-07-13 23:05:54 +09:30
Jon
61a1792e4c refactor(core): Switch to inherit from Centurion model for model TicketBase
ref: #870 #861
2025-07-13 23:03:49 +09:30
Jon
3baed3f8f5 refactor(core): Add fn get_organization to centurion mixin
Fn is required for perms to work correctly

ref: #870
2025-07-13 22:30:15 +09:30
Jon
9a516fbebc Merge pull request #862 from nofusscomputing/refactor-switch-model-inheritence 2025-07-13 21:36:52 +09:30
Jon
be708b1244 test(fixture): if item already exists, when fetching remove modified field from query if not found with
field is an auto field and most likely wont match the existing item

ref: #862
2025-07-13 21:11:55 +09:30
Jon
2f7865bb83 feat(access): Add Audit and notes tables for model Role
ref: #862 #844
2025-07-12 18:27:03 +09:30
Jon
b643347c01 test(access): Model Role is not usable within global org, remove test
ref: #862 #844
2025-07-12 18:21:51 +09:30
Jon
957052da84 refactor(access): Adjust add permission test for model Role
ref: #862 #844
2025-07-12 17:30:58 +09:30
Jon
b2e0982b4c refactor(access): Migrations for Inheritance change for Role model
ref: #862 closes #844
2025-07-12 16:57:20 +09:30
Jon
ca63e4d6fc refactor(access): Update URL route name for Role model
ref: #862 #844
2025-07-12 16:56:41 +09:30
Jon
3b7e88969e refactor(access): Update Test Suite for Role model
ref: #862 #844
2025-07-12 16:56:07 +09:30
Jon
953dce2984 feat(access): Add AuditHistory Serializer for Role model
ref: #862 #844
2025-07-12 16:44:43 +09:30
Jon
e0c8ef46f5 feat(access): Add Notes Serializer for Role model
ref: #862 #844
2025-07-12 16:43:58 +09:30
Jon
548051df6d refactor(access): Switch to inherit from Centurion model for model Role
ref: #862 #844
2025-07-12 16:42:59 +09:30
Jon
d6eb33141a refactor: Asset and ITAM Asset must use url kwarg model_name not asset_model
ref: #862 closes #857 #858
2025-07-12 16:13:08 +09:30
Jon
4f36769fba refactor(accounting): Update existing tests to work due to model inheritance changes
ref: #862 #857
2025-07-10 20:35:32 +09:30
Jon
018004ccb9 refactor(itam): Update URL route name for ITAMAssetBase model
ref: #862 #858
2025-07-10 19:54:28 +09:30
Jon
b851388d37 refactor(itam): Update Test Suite for ITAMAssetBase model
ref: #862 #858
2025-07-10 19:53:38 +09:30
Jon
38b4542a58 feat(itam): Add AuditHistory Serializer for ITAMAssetBase model
ref: #862 #858
2025-07-10 19:51:13 +09:30
Jon
3b5673ae4b feat(itam): Add Notes Serializer for ITAMAssetBase model
ref: #862 #858
2025-07-10 19:50:53 +09:30
Jon
e4e7ad915b refactor(itam): Switch to inherit from Centurion model for model ITAMAssetBase
ref: #862 #858
2025-07-10 19:50:04 +09:30
Jon
de7c52d733 refactor(accounting): Switch to inherit from Centurion model for model AssetBase
ref: #862 #857
2025-07-10 19:48:21 +09:30
Jon
d741992b6d refactor(accounting): Update URL route name for AssetBase model
ref: #862 #857
2025-07-10 19:47:18 +09:30
Jon
9b3c8d2225 refactor(accounting): Update Test Suite for AssetBase model
ref: #862 #857
2025-07-10 19:43:54 +09:30
Jon
4ff457a58c feat(accounting): Add AuditHistory Serializer for AssetBase model
ref: #862 #857
2025-07-10 19:33:36 +09:30
Jon
46b4df5dc1 feat(accounting): Add Notes Serializer for AssetBase model
ref: #862 #857
2025-07-10 19:32:57 +09:30
Jon
2482148466 refactor(accounting): Switch to inherit from Centurion model for model AssetBase
ref: #862 #857
2025-07-10 19:28:41 +09:30
Jon
2c1c769988 Merge pull request #860 from nofusscomputing/fix-stuff 2025-07-09 11:18:54 +09:30
Jon
38f3a3bf05 fix(core): Include model so content type is created
ref: #860
2025-07-09 10:54:59 +09:30
Jon
965bde51bc Merge pull request #856 from nofusscomputing/test-functional-get-working 2025-07-07 21:24:30 +09:30
Jon
303cc31f6a test(devops): skip Model History entry test as it should be done as part of serializer and viewset
ref: #856 #781
2025-07-07 20:55:22 +09:30
Jon
97f99abe2c chore: ensure api post items for tests is the correct type
ref: #856
2025-07-07 18:51:59 +09:30
Jon
93642ee6a1 Merge pull request #855 from nofusscomputing/test-get-functional-working 2025-07-07 00:41:19 +09:30
Jon
eeaf3ab5fc test(devops): update no_org_serializer test so it works for model SoftwareEnableFeatureFlag
ref: #855 #812
2025-07-07 00:16:17 +09:30
Jon
5b6f9bec00 chore(tests): cater for manytomany for api fields
ref: #855
2025-07-07 00:16:11 +09:30
Jon
cf7fb57583 chore(itam): Ensure when creating Model ITAMAsset that the asset number field is populated
ref: #855 #737
2025-07-06 22:48:47 +09:30
Jon
45abdc2e00 feat: When attempting to create and objetc must be unique and alrready exists, dont return error return existing object
ref: #855
2025-07-06 22:20:54 +09:30
Jon
22ad79386e chore(settings): Remove old AppSettings model permission tests
ref: #855 #834
2025-07-06 20:59:03 +09:30
Jon
baabf84234 fix(settings): AppSettings requires super user perms
ref: #855 #834
2025-07-06 20:55:18 +09:30
Jon
c54de5c627 feat(access): History + Notes model migrations for Company Model
ref: #855 #801
2025-07-06 20:23:48 +09:30
Jon
803d7f3223 test(itam): Model DeviceOperatingSystem is not multi-org based skip those tests
ref: #855 #801
2025-07-06 19:14:13 +09:30
Jon
b5f55c1eea test(settings): Model UserSettings does not allowing adding rows, skip test
ref: #855 #836
2025-07-06 19:06:57 +09:30
Jon
888fc68180 test(settings): Model AppSettings does not allowing adding rows, skip test
ref: #855 #834
2025-07-06 19:06:20 +09:30
Jon
5d8062fcef Merge pull request #849 from nofusscomputing/test-model-inheritance-refactor 2025-07-04 19:51:58 +09:30
Jon
e99ab1b0c6 test(fixture): Ensure _meta attribute exists when cleaning up models prior to attempting to use
ref: #849
2025-07-04 19:30:33 +09:30
Jon
7c85d9653f revert(api): when testing create object, remove the actual created object prior to testing the add
object is class scoped and cant be deleted

ref: #849
2025-07-04 19:06:48 +09:30
Jon
9ebf9bf3de refactor(api): dont query db for instance, use existing from response
ref: #849
2025-07-04 18:42:51 +09:30
Jon
57936073db feat(api): map notfound and perm denied django -> drf exceptions
ref: #849
2025-07-04 17:57:08 +09:30
Jon
638e2ba986 chore: add notation to capture unknwon exception for capture
ref: #849
2025-07-04 13:28:55 +09:30
Jon
6af615f918 test(devops): SoftwareEnableFeatureFlagging model does not use global org, so dont test global org return
ref: #849
2025-07-04 13:26:13 +09:30
Jon
37a69888b6 test(api): when testing create object, remove the actual created object prior to testing the add
ref: #849
2025-07-04 13:26:07 +09:30
Jon
c2255a5834 test(fixture): when creating object and it exists, rtn that object
ref: #849
2025-07-04 13:25:35 +09:30
Jon
77cf598e4d fix(api): Convert Django Exceptions to DRF API Exception equivilent
ref: #849 #854
2025-07-04 13:23:31 +09:30
Jon
1f4ce92623 test(devops): If test publically accessable, dont test by user org only as test is NA
ref: #849 #782
2025-07-04 10:10:15 +09:30
Jon
882ae90d83 fix(api): Ensure if exception DRF, message returned is from that exception
ref: #849
2025-07-04 10:03:48 +09:30
Jon
ea88076d7f test(settings): UserSettings perms tests are for the user that is accessing them
ref: #849 #836
2025-07-04 09:45:50 +09:30
Jon
6e925e8456 test(settings): UserSettings perms tests are not org based, skip those tests
ref: #849 #836
2025-07-04 09:38:34 +09:30
Jon
ffaac107f4 refactor(api): additional perms tests if they exist must be inc first
ref: #849
2025-07-04 09:38:26 +09:30
Jon
b282162a86 test(settings): AppSettings perms tests are not org based, skip those tests
ref: #849 #834
2025-07-04 09:38:07 +09:30
Jon
05fd82459a chore: Add makemigrations debug launch to vscode
ref: #849
2025-07-04 08:41:40 +09:30
Jon
b2383a2236 refactor(devops): remove ViewSet get_queryset function
not required as the model is set to the actual model

ref: #849 #809
2025-07-04 08:41:40 +09:30
Jon
bf1bb23dd5 chore(test): Add required fields to service fixture
ref: #849 #828
2025-07-04 08:41:40 +09:30
Jon
73a704d5e3 fix(devops): git repository is sub-model ViewSet must inherit from SubModel
ref: #848 #809 #810 #811
2025-07-04 08:40:36 +09:30
Jon
7b22541002 chore(access): Update common viewset test suite mock request
ref: #847 #840 #841 #842 #843 #845
2025-07-04 08:40:36 +09:30
Jon
1ba7661f07 refactor(access): Update Entity model ViewSet attribute model_kwarg to model_name
ref: #847 #840 #841 #842 #843 #845
2025-07-04 08:40:36 +09:30
Jon
2eb0ba674e refactor(access): Update Entity model ViewSet to inherit from submodel-rewrite
ref: #847 #840 #841 #842 #843 #845
2025-07-04 08:40:36 +09:30
Jon
6ae3b193fa chore(access): remove get_url_kwargs function as it's not required
ref: #847 #840 #841 #842 #843 #845
2025-07-04 08:40:36 +09:30
Jon
2b7eaace4d fix(access): entity field entity_type is an auto field
ref: #847
2025-07-04 08:40:36 +09:30
Jon
4f8fca9aff chore: Ensure employee fixture creates integer within max of field
ref: #847
2025-07-04 08:40:36 +09:30
Jon
53a474fa2a chore: update apifield test suit to set model class var and for the removal of fields property from models
ref: #847
2025-07-04 08:40:36 +09:30
Jon
b549059d15 chore(test): update db fixture
ref: #839
2025-07-04 08:40:36 +09:30
Jon
e7eb1442b7 docs(issue_template): remove notes and history requirements from new model issue template
ref: #839
2025-07-04 08:40:36 +09:30
Jon
6d2406ae1c chore(access): Add pytest mark for Model Entity Tests
ref: #839 closes #840
2025-07-04 08:40:36 +09:30
Jon
aa86f04aa2 chore(access): Add pytest mark for Model Company Tests
ref: #839 closes #845
2025-07-04 08:40:36 +09:30
Jon
6d33653db0 feat(human_resources): Add model tag for Employee model
ref: #839 closes #843
2025-07-04 08:40:36 +09:30
Jon
bc3545d53e refactor(access): Update Test Suite for Employee model
ref: #839 #843
2025-07-04 08:40:36 +09:30
Jon
3ea44aaabe feat(human_resources): Add AuditHistory Serializer for Employee model
ref: #839 #843
2025-07-04 08:40:36 +09:30
Jon
d7442cfff4 feat(human_resources): Add Notes Serializer for Employee model
ref: #839 #843
2025-07-04 08:40:36 +09:30
Jon
23832fff6a feat(human_resources): Change model to inherit from CenturionModel for Employee model
ref: #839 #843
2025-07-04 08:40:36 +09:30
Jon
c5f8412d46 feat(access): Add model tag for Person model
ref: #839 closes #841
2025-07-04 08:40:36 +09:30
Jon
a582cbbe59 refactor(access): Update Test Suite for Person model
ref: #839 #841
2025-07-04 08:40:36 +09:30
Jon
8c48fb6c37 feat(access): Add AuditHistory Serializer for Person model
ref: #839 #841
2025-07-04 08:40:36 +09:30
Jon
3c6589f07c feat(access): Add Notes Serializer for Person model
ref: #839 #841
2025-07-04 08:40:36 +09:30
Jon
d1fbead077 feat(access): Change model to inherit from CenturionModel for Person model
ref: #839 #841
2025-07-04 08:40:36 +09:30
Jon
3025c397b5 feat(access): Add model tag for Contact model
ref: #839 closes #842
2025-07-04 08:40:36 +09:30
Jon
43490b7407 refactor(access): Update Test Suite for Contact model
ref: #839 #842
2025-07-04 08:40:36 +09:30
Jon
e15bb11a76 feat(access): Add AuditHistory Serializer for Contact model
ref: #839 #842
2025-07-04 08:40:36 +09:30
Jon
1823cb4ec1 feat(access): Add Notes Serializer for Contact model
ref: #839 #842
2025-07-04 08:40:36 +09:30
Jon
9de99e07f7 feat(access): Change model to inherit from CenturionModel for Contact model
ref: #839 #842
2025-07-04 08:40:36 +09:30
Jon
c9c8cf6700 refactor(access): Update Test Suite for Company model
ref: #839 #845 #760
2025-07-04 08:40:36 +09:30
Jon
af868fcf4f feat(access): Add AuditHistory Serializer for Company model
ref: #839 #845 #760
2025-07-04 08:40:36 +09:30
Jon
e9fe6a2bdc feat(access): Add Notes Serializer for Entity model
ref: #839 #845 #760
2025-07-04 08:40:36 +09:30
Jon
595b232db9 feat(access): Change model to inherit from CenturionModel for Company model
ref: #839 #845 #760
2025-07-04 08:40:36 +09:30
Jon
87d0a5174f feat(access): Change model to inherit from CenturionModel for Entity model
ref: #839 closes #840
2025-07-04 08:40:36 +09:30
Jon
f074abefa7 refactor(access): Update URL route name for Entity model
ref: #839 #840 #841 #842 #843 #844 #845
2025-07-04 08:40:36 +09:30
Jon
b6dae72a07 refactor(access): Update Test Suite for Entity model
ref: #839 #840
2025-07-04 08:40:36 +09:30
Jon
847e4f93ea feat(access): Add AuditHistory Serializer for Entity model
ref: #839 #840
2025-07-04 08:40:36 +09:30
Jon
3943171534 feat(access): Add Notes Serializer for Entity model
ref: #839 #840
2025-07-04 08:40:36 +09:30
Jon
ef5d132ab3 feat(access): Change model to inherit from CenturionModel for Entity model
ref: #839 #840
2025-07-04 08:40:36 +09:30
Jon
874ce0c4f4 chore: enable creating reandom email field in model_kwarg_data fixture
ref: #839
2025-07-04 08:40:36 +09:30
Jon
ae20451033 chore: make model_instance a function fixture
ref: #839
2025-07-04 08:40:36 +09:30
Jon
fefb7e67a0 test(settings): for api checks for model AppSettings, make user super_user
required so that tests can run

ref: #839
2025-07-04 08:40:36 +09:30
Jon
41da9fb6a0 test(settings): Exclude inter-org tests for model AppSettings
perms for appsettings are global

ref: #839 #834
2025-07-04 08:40:36 +09:30
Jon
5234051538 refactor(access): Update is_tenancy_object to check for CenturionModel
ref: #839 #767
2025-07-04 08:40:36 +09:30
Jon
ebea2da906 test(settings): Remove old API Permission tests no longer required
ref: #833 #836
2025-07-04 08:40:36 +09:30
Jon
e233bf4487 test(settings): Ensure ExternalLink model hasrequired field template added
ref: #833 #835
2025-07-04 08:40:36 +09:30
Jon
ea1d6f4a20 chore(api): Add support to API Permission to include additional tests
ref: #833
2025-07-04 08:40:36 +09:30
Jon
0a9a26a43b refactor(access): For request middleware, use filter and first object so that testing can occur when mre than one exists
ref: #833
2025-07-04 08:40:36 +09:30
Jon
8f984e6c21 test(api): if model lacks list endpoint, check if method alllowed for test cases for Functional API perms test suite
ref: #833
2025-07-04 08:40:36 +09:30
Jon
7e3ae4c0bc test(api): if model lacks organization field, xfail returned orgs test cases for Functional API perms test suite
ref: #833
2025-07-04 08:40:36 +09:30
Jon
ef6ebcdcf4 fix(access): Ensure that if method not allowed, exception is thrown first before perms check
ref: #833
2025-07-04 08:40:36 +09:30
Jon
0b8b0bc850 test: Ensure service fixture assosiates with device
ref: #833 #828
2025-07-04 08:40:35 +09:30
Jon
e3804b4f9b test(api): if model lacks organization field, xfail returned orgs test cases for Functional API perms test suite
ref: #833 #820
2025-07-04 08:40:35 +09:30
Jon
ded80d4a8c fix(itam): Model software must be related linked to organization model
ref: #833 #820
2025-07-04 08:40:35 +09:30
Jon
2027f3fe97 test: Add depreciated models to be excluded from coverage
ref: #833
2025-07-04 08:40:35 +09:30
Jon
239540c9ab chore: correct docs linting error
ref: #833
2025-07-04 08:40:35 +09:30
Jon
164aa21fad feat(settings): Add model tag for ExtrnalLink model
ref: #833 #835 closes #584
2025-07-04 08:40:35 +09:30
Jon
78539f2551 refactor(settings): Update URL route name for UserSettings model
ref: #833 closes #836
2025-07-04 08:40:35 +09:30
Jon
095239f199 refactor(settings): Update Test Suite for ExternalLink model
ref: #833 #836
2025-07-04 08:40:35 +09:30
Jon
ea8c81c64d feat(settings): Add AuditHistory Serializer for UserSettings model
ref: #833 #836
2025-07-04 08:40:35 +09:30
Jon
80770deafd feat(settings): Add Notes Serializer for UserSettings model
ref: #833 #836
2025-07-04 08:40:35 +09:30
Jon
05a32711b5 feat(settings): Change model to inherit from CenturionModel for UserSettings model
ref: #833 #836
2025-07-04 08:40:35 +09:30
Jon
1fe669e932 feat(settings): Add model ExternalLink to migrate for history and notes
ref: #833 closes #835
2025-07-04 08:40:35 +09:30
Jon
801bac9e6d refactor(settings): Update URL route name for ExternalLink model
ref: #833 #835
2025-07-04 08:40:35 +09:30
Jon
4a3f2ae6db refactor(settings): Update Test Suite for ExternalLink model
ref: #833 #835
2025-07-04 08:40:35 +09:30
Jon
fdbf9f81db feat(settings): Add AuditHistory Serializer for ExternalLink model
ref: #833 #835
2025-07-04 08:40:35 +09:30
Jon
b1fcc58741 feat(settings): Add Notes Serializer for ExternalLink model
ref: #833 #835
2025-07-04 08:40:35 +09:30
Jon
278b9e5c08 feat(settings): Change model to inherit from CenturionModel for ExternalSettings model
ref: #833 #835
2025-07-04 08:40:35 +09:30
Jon
cec4b39f5a feat(settings): Add model AppSettings to migrate for history and notes
ref: #833 closes #834
2025-07-04 08:40:35 +09:30
Jon
c264ab2cd4 refactor(settings): Update URL route name for AppSettings model
ref: #833 #834
2025-07-04 08:40:35 +09:30
Jon
44688c35df fix(access): if user has no orgs, dont filter by for query
ref: #833
2025-07-04 08:40:35 +09:30
Jon
f023901dc4 refactor(settings): Update Test Suite for AppSettings model
ref: #833 #834
2025-07-04 08:40:35 +09:30
Jon
c53a488a27 feat(settings): Add AuditHistory Serializer for AppSettings model
ref: #833 #834
2025-07-04 08:40:35 +09:30
Jon
51f7b94360 feat(settings): Add Notes Serializer for AppSettings model
ref: #833 #834
2025-07-04 08:40:35 +09:30
Jon
f7e71be426 feat(settings): Change model to inherit from CenturionModel for AppSettings model
ref: #833 #834
2025-07-04 08:40:35 +09:30
Jon
39bcd74c56 chore: Add support for testing tenant model to fixture model_instance
ref: #833
2025-07-04 08:40:35 +09:30
Jon
763836dd79 chore: skip depreciated linked ticket tests
ref: #833
2025-07-04 08:40:35 +09:30
Jon
3667ed76d3 feat(project_management): Add model ProjectType to migrate for history and notes
ref: #833 closes #832
2025-07-04 08:40:35 +09:30
Jon
f62fa6d166 refactor(project_management): Update URL route name for ProjectType model
ref: #833 #832
2025-07-04 08:40:35 +09:30
Jon
caf2fd0ad3 refactor(project_management): Update Test Suite for ProjectType model
ref: #833 #832
2025-07-04 08:40:35 +09:30
Jon
c7dda4110a feat(project_management): Add AuditHistory Serializer for ProjectTYpe model
ref: #833 #832
2025-07-04 08:40:35 +09:30
Jon
33bba65b44 feat(project_management): Add Notes Serializer for ProjectType model
ref: #833 #832
2025-07-04 08:40:35 +09:30
Jon
c7595f71ea feat(project_management): Change model to inherit from CenturionModel for ProjectType model
ref: #830 #832 closes #594
2025-07-04 08:40:35 +09:30
Jon
3e6570045c feat(project_management): Add model ProjectState to migrate for history and notes
ref: #833 closes #831
2025-07-04 08:40:35 +09:30
Jon
461b3f4933 refactor(project_management): Update URL route name for ProjectState model
ref: #833 #831
2025-07-04 08:40:35 +09:30
Jon
4c53c92a22 refactor(project_management): Update Test Suite for ProjectState model
ref: #833 #831
2025-07-04 08:40:35 +09:30
Jon
fbe5675bc9 feat(project_management): Add AuditHistory Serializer for ProjectState model
ref: #833 #831
2025-07-04 08:40:35 +09:30
Jon
f1f70e9a22 feat(project_management): Add Notes Serializer for ProjectState model
ref: #833 #831
2025-07-04 08:40:35 +09:30
Jon
e5946cecbc feat(project_management): Change model to inherit from CenturionModel for ProjectState model
ref: #830 #831
2025-07-04 08:40:35 +09:30
Jon
1af06bbaa6 feat(project_management): Add model ProjectMilestone to migrate for history and notes
ref: #833 closes #830
2025-07-04 08:40:35 +09:30
Jon
ff77aaac13 refactor(project_management): Update URL route name for ProjectMilestone model
ref: #833 #830
2025-07-04 08:40:35 +09:30
Jon
c07b208a63 refactor(project_management): Update Test Suite for ProjectMilestone model
ref: #833 #830
2025-07-04 08:40:35 +09:30
Jon
cc82c80639 feat(project_management): Add AuditHistory Serializer for ProjectMilestone model
ref: #833 #830
2025-07-04 08:40:35 +09:30
Jon
ba26a18682 feat(project_management): Add Notes Serializer for ProjectMilestone model
ref: #833 #830
2025-07-04 08:40:35 +09:30
Jon
c9a21bc3e9 feat(project_management): Change model to inherit from CenturionModel for ProjectManagement model
ref: #833 #830 closes #591
2025-07-04 08:40:35 +09:30
Jon
60b38cc61b chore: correct tests
ref: #824
2025-07-04 08:40:34 +09:30
Jon
936c6b4cd6 chore: fix accessing var that dont exist
ref: #824
2025-07-04 08:40:34 +09:30
Jon
6a7dd54c58 chore: remove depreciated docs
ref: #824 #343 #757
2025-07-04 08:40:34 +09:30
Jon
3cbe4201d0 chore: fix accessing var that dont exist
ref: #824
2025-07-04 08:40:34 +09:30
Jon
253ee3822a feat(project_management): Add model Project to migrate for history and notes
ref: #824 closes #829
2025-07-04 08:40:34 +09:30
Jon
bf7fa83e39 refactor(project_management): Update URL route name for Project model
ref: #824 #829
2025-07-04 08:40:34 +09:30
Jon
6c1eee1d98 refactor(project_management): Update Test Suite for Project model
ref: #824 #829
2025-07-04 08:40:34 +09:30
Jon
dc3d02eb66 feat(project_management): Add AuditHistory Serializer for Project model
ref: #824 #829
2025-07-04 08:40:34 +09:30
Jon
afb688a396 feat(project_management): Add Notes Serializer for Project model
ref: #824 #829
2025-07-04 08:40:34 +09:30
Jon
b378565ff6 feat(project_management): Change model to inherit from CenturionModel for Project model
ref: #824 #829 closes #590
2025-07-04 08:40:34 +09:30
Jon
a9cde74013 feat(itim): Add model Service to migrate for history and notes
ref: #824 closes #828
2025-07-04 08:40:34 +09:30
Jon
27f35e6fde refactor(itim): Update URL route name for Service model
ref: #824 #828
2025-07-04 08:40:34 +09:30
Jon
f377ad102e refactor(itim): Update Test Suite for Service model
ref: #824 #828
2025-07-04 08:40:34 +09:30
Jon
fa7c70f2ce feat(itim): Add AuditHistory Serializer for Service model
ref: #824 #828
2025-07-04 08:40:34 +09:30
Jon
1f6a36ec8e feat(itim): Add Notes Serializer for Service model
ref: #824 #828
2025-07-04 08:40:34 +09:30
Jon
33617bd608 feat(itim): Change model to inherit from CenturionModel for Service model
ref: #824 #828
2025-07-04 08:40:34 +09:30
Jon
b83a087081 feat(itim): Add model Port to migrate for history and notes
ref: #824 closes #827
2025-07-04 08:40:34 +09:30
Jon
8fd243e691 refactor(itim): Update URL route name for Port model
ref: #824 #827
2025-07-04 08:40:34 +09:30
Jon
b20cab4f8e refactor(itim): Update Test Suite for Port model
ref: #824 #827
2025-07-04 08:40:34 +09:30
Jon
f25c7274c6 feat(itim): Add AuditHistory Serializer for Port model
ref: #824 #827
2025-07-04 08:40:34 +09:30
Jon
1e339a1f0d feat(itim): Add Notes Serializer for Port model
ref: #824 #827
2025-07-04 08:40:34 +09:30
Jon
00c3eff30e feat(itim): Change model to inherit from CenturionModel for Port model
ref: #824 #827 closes #589
2025-07-04 08:40:34 +09:30
Jon
6163b4ad2d refactor(itim): Update URL route name for ClusterType model
ref: #824 closes #826 #580
2025-07-04 08:40:34 +09:30
Jon
8452e678da refactor(itim): Update Test Suite for ClusterType model
ref: #824 #826
2025-07-04 08:40:34 +09:30
Jon
30c19031eb feat(itim): Add AuditHistory Serializer for ClusterType model
ref: #824 #826
2025-07-04 08:40:34 +09:30
Jon
d0e8b58176 feat(itim): Add Notes Serializer for ClusterType model
ref: #824 #826
2025-07-04 08:40:34 +09:30
Jon
a192659c80 feat(itim): Change model to inherit from CenturionModel for ClusterType model
ref: #824 #826
2025-07-04 08:40:34 +09:30
Jon
44489b308b feat(itim): Add model Cluster to migrate for history and notes
ref: #824 closes #825
2025-07-04 08:40:34 +09:30
Jon
1b875233da refactor(itim): Update URL route name for Cluster model
ref: #824 #825
2025-07-04 08:40:34 +09:30
Jon
93eb65316e refactor(itim): Update Test Suite for Cluster model
ref: #824 #825
2025-07-04 08:40:34 +09:30
Jon
1611537d86 feat(itim): Add Notes Serializer for Cluster model
ref: #824 #825
2025-07-04 08:40:34 +09:30
Jon
5da4e2338a feat(itim): Add AuditHistory Serializer for Cluster model
ref: #824 #825
2025-07-04 08:40:34 +09:30
Jon
07ef410c31 feat(itim): Change model to inherit from CenturionModel for Cluster model
ref: #824 #825
2025-07-04 08:40:34 +09:30
Jon
067aa8714f revert(core): return settings_value template tag
ref: #824
2025-07-04 08:40:34 +09:30
Jon
8c61c4e8a6 feat(itam): Add model SoftwareVersion to migrate for history and notes
ref: #823 closes #822
2025-07-04 08:40:34 +09:30
Jon
234e85368a refactor(itam): Update Test Suite for SoftwareVersion model
ref: #823 #822
2025-07-04 08:40:34 +09:30
Jon
915101820e refactor(itam): Update URL route name for SoftwareVersion model
ref: #823 #822
2025-07-04 08:40:34 +09:30
Jon
93992dbef5 feat(itam): Add Notes Serializer for SoftwareVersiony model
ref: #823 #822
2025-07-04 08:40:34 +09:30
Jon
81e7d21d6f feat(itam): Add AuditHistory Serializer for SoftwareVersion model
ref: #823 #822
2025-07-04 08:40:34 +09:30
Jon
ae4b2a3c04 feat(itam): Change model to inherit from CenturionModel for SoftwareVersion model
ref: #823 #822
2025-07-04 08:40:34 +09:30
Jon
cb0d095eb7 feat(itam): Add model SoftwareCategory to migrate for history and notes
ref: #823 closes #821
2025-07-04 08:40:33 +09:30
Jon
b491ba45e5 refactor(itam): Update Test Suite for SoftwareCategory model
ref: #823 #821
2025-07-04 08:40:33 +09:30
Jon
610e4665a6 refactor(itam): Update URL route name for SoftwareCategory model
ref: #823 #820
2025-07-04 08:40:33 +09:30
Jon
c2ad9e3189 feat(itam): Add Notes Serializer for SoftwareCategory model
ref: #823 #821
2025-07-04 08:40:33 +09:30
Jon
0d2b2d2183 feat(itam): Add AuditHistory Serializer for SoftwareCategory model
ref: #823 #821
2025-07-04 08:40:33 +09:30
Jon
851bf553b0 feat(itam): Change model to inherit from CenturionModel for SoftwareCategory model
ref: #823 #821 closes #595
2025-07-04 08:40:33 +09:30
Jon
d6da2cf0fb feat(itam): Add model Software to migrate for history and notes
ref: #823 #820
2025-07-04 08:40:33 +09:30
Jon
ecc544ce19 refactor: cater for dev that does not exist in test cleanup
ref: #823
2025-07-04 08:40:33 +09:30
Jon
7542521866 refactor(itam): Update Test Suite for Software model
ref: #823 closes #820
2025-07-04 08:40:33 +09:30
Jon
df43ee9e7a refactor(itam): Update URL route name for Software model
ref: #823 #820
2025-07-04 08:40:33 +09:30
Jon
bdbcc46db5 feat(itam): Add Notes Serializer for Software model
ref: #823 #820
2025-07-04 08:40:33 +09:30
Jon
f8e659ddd5 feat(itam): Add AuditHistory Serializer for Software model
ref: #823 #820
2025-07-04 08:40:33 +09:30
Jon
e87e3992aa feat(itam): Change model to inherit from CenturionModel for Software model
ref: #823 #820
2025-07-04 08:40:33 +09:30
Jon
61672c04e3 chore: add pytest mark for os version unit tests
ref: #823 #819
2025-07-04 08:40:33 +09:30
Jon
8ca2513952 feat(itam): Add model OperatingSystemVersion to migrate for history and notes
ref: #823 closes #819
2025-07-04 08:40:33 +09:30
Jon
0b1d658db9 refactor(itam): Update Test Suite for OperatingSystemVersion model
ref: #823 #819
2025-07-04 08:40:33 +09:30
Jon
e60081fea6 feat(itam): Add Notes Serializer for OperatingSystemVersion model
ref: #823 #819
2025-07-04 08:40:33 +09:30
Jon
7ee6970615 feat(itam): Add AuditHistory Serializer for OperatingSystemVersion model
ref: #823 #819
2025-07-04 08:40:33 +09:30
Jon
580df01cc5 feat(itam): Change model to inherit from CenturionModel for OperatingSystemVersion model
ref: #823 #819 closes #588
2025-07-04 08:40:33 +09:30
Jon
4a38f73ddf chore(itam): add missing pytest mark serializer
ref:
2025-07-04 08:40:33 +09:30
Jon
c29e5890eb chore(itam): add missing import
ref:#817 #818
2025-07-04 08:40:33 +09:30
Jon
175ab02a7d feat(itam): Add model OperatingSystem to migrate for history and notes
ref:#817 closes #818
2025-07-04 08:40:33 +09:30
Jon
434992323f refactor(itam): Update Test Suite for OperatingSystem model
ref:#817 #818
2025-07-04 08:40:33 +09:30
Jon
7cbe13ffe4 refactor(itam): Update URL route name for DeviceSoftware model
ref:#817 #818
2025-07-04 08:40:33 +09:30
Jon
5fc4d7da91 feat(itam): Add Note Serializer for DeviceSoftware model
ref:#817 #818
2025-07-04 08:40:33 +09:30
Jon
1a598fc917 feat(itam): Add AuditHistory Serializer for DeviceSoftware model
ref:#817 #818
2025-07-04 08:40:33 +09:30
Jon
465c0d8679 feat(itam): Change model to inherit from CenturionModel for DeviceSoftware model
ref:#817 #818
2025-07-04 08:40:33 +09:30
Jon
d7e70c1e62 refactor(itam): Update Test Suite for DeviceSoftware model
ref:#817 closes #802
2025-07-04 08:40:33 +09:30
Jon
48cf7b56c4 feat(itam): Change model to inherit from Centurion for DeviceSoftware model
ref:#817 #802
2025-07-04 08:40:33 +09:30
Jon
fbc2bf081e refactor(itam): Update Test Suite for DeviceDeviceOperatingSystem model
ref:#817 closes #801
2025-07-04 08:40:33 +09:30
Jon
4aaf2f87ef refactor(itam): Update URL route for DeviceDeviceOperatingSystem model
ref:#817 #801
2025-07-04 08:40:33 +09:30
Jon
32ffe34cf3 refactor(itam): Migration for updating model inheritance for DeviceDeviceOperatingSystem model
ref:#817 #801
2025-07-04 08:40:33 +09:30
Jon
281e233a7a refactor(itam): Updated Unit model test suite for DeviceType model
ref:#817 closes #803
2025-07-04 08:40:33 +09:30
Jon
b5de17441b feat(itam): Add model_tag to DeviceType model
ref:#817 #803 closes #581 #582
2025-07-04 08:40:33 +09:30
Jon
731c069c0b refactor(devops): Updated Unit model test suite for DeviceModel model
ref:#817 closes #800
2025-07-04 08:40:33 +09:30
Jon
729bcf9a57 refactor(devops): Migration for updating model inheritance for DeviceModel model
ref:#817 #800
2025-07-04 08:40:33 +09:30
Jon
3332674cb6 refactor(itam): Updated Unit model test suite for Device model
ref:#817 closes #799
2025-07-04 08:40:33 +09:30
Jon
306d4b5b7a feat(itam): Add DeviceType for history and notes data migration
ref:#817 #803
2025-07-04 08:40:33 +09:30
Jon
69e6177700 feat(itam): Add DeviceModel for history and notes data migration
ref:#817 #800
2025-07-04 08:40:33 +09:30
Jon
5e12ea7a57 feat(itam): Add DEvice for history and notes data migration
ref:#817 #799
2025-07-04 08:40:33 +09:30
Jon
5d56837f1e refactor(devops): Updated Unit model test ssuite for SoftwareEnabledFeatureFlag model
ref:#817 closes #812
2025-07-04 08:40:33 +09:30
Jon
dabf663651 refactor(devops): Migration for updating model inheritance for SoftwareEnabledFeatureFlag model
ref:#817 #812
2025-07-04 08:40:33 +09:30
Jon
1841023896 refactor(devops): Update url route basename for SoftwareEnabledFeatureFlag model
ref:#817 #812
2025-07-04 08:40:32 +09:30
Jon
d48c19a8ed feat(devops): Switch SoftwareEnabledFeatureFlag model to inherit from CenturionModel
ref: #817 #812
2025-07-04 08:40:32 +09:30
Jon
4f5a4c4124 chore: dont set model id for an instance during testing
ref: #813
2025-07-04 08:40:32 +09:30
Jon
f44fb28b8d chore: correct pytest model marks
ref: #813
2025-07-04 08:40:32 +09:30
Jon
a241ba61b0 chore(core): correct test for get_url_kwargs for meta abstract model
ref: #813
2025-07-04 08:40:32 +09:30
Jon
dbf67f7737 fix(devops): Ensure mandatory fields are writeable for model GitRepository
ref: #813 #515
2025-07-04 08:40:32 +09:30
Jon
da865fd5a8 refactor(tests): make all parameterized_ vars properties
ref: #813
2025-07-04 08:40:32 +09:30
Jon
446ea7c910 chore(core): correct test for get_url_kwargs for meta abstract model
ref: #813 #793
2025-07-04 08:40:32 +09:30
Jon
d4de500593 chore(config_management): Mark get_url methods as xfail for abstract models
ref: #813 #793
2025-07-04 08:40:32 +09:30
Jon
f81133f96c chore(config_management): ConfigGroupHost has no endpoint
ref: #813 #793
2025-07-04 08:40:32 +09:30
Jon
40e240c15b test(api): Update Functional API Permission test suite to cater for public RO endpoints
ref: #813 #767 #729
2025-07-04 08:40:32 +09:30
Jon
c5823152ca feat(devops): Update checkin model fixture so it creates the feature flag
ref: #813 #808
2025-07-04 08:40:32 +09:30
Jon
b17fcf0eac feat(devops): Add methods get_url and get_url_kwargs to CheckIn model
ref: #813 #808
2025-07-04 08:40:32 +09:30
Jon
9a737ac1c4 chore: correct inheritance order
ref: #813
2025-07-04 08:40:32 +09:30
Jon
b764ebf6e1 test(core): Ensure model mehod get_url_kwargs returns a dict for all Centurion Models
ref: #813 closes #809 #810 #811
2025-07-04 08:40:32 +09:30
Jon
6193fe0418 feat(devops): Add migration to signal
ref: #813 #811
2025-07-04 08:40:32 +09:30
Jon
18a4158f3e feat(devops): Add migration to signal
ref: #813 #810
2025-07-04 08:40:32 +09:30
Jon
82d1e9995f test(devops): Add GitLabRepository Unit Model test suite
ref: #813 #811
2025-07-04 08:40:32 +09:30
Jon
e7942a79ed chore: update test db fixture
ref: #807
2025-07-04 08:40:32 +09:30
Jon
7378b103e1 test(devops): Add GitHubRepository Unit Model test suite
ref: #807 #515 #810
2025-07-04 08:40:32 +09:30
Jon
07b6a2f4e2 test(devops): Add GitRepository Unit Model test suite
ref: #807 #515 #809
2025-07-04 08:40:32 +09:30
Jon
8f0a5386af feat(devops): Add migration to signal
ref: #807 #515 #809
2025-07-04 08:40:32 +09:30
Jon
2cb334f25f feat(devops): Add migration to signal
ref: #807 #515 #810
2025-07-04 08:40:32 +09:30
Jon
b4daa61b36 feat(devops): Update URL route basename
ref: #807 #515 #809 #810 #811
2025-07-04 08:40:32 +09:30
Jon
0e5b5db7b9 feat(devops): Migrations for switching GitLabRepository model to inherit from CenturionModel
ref: #807 #515 #811
2025-07-04 08:40:32 +09:30
Jon
0daf4a5316 feat(devops): Migrations for switching GitRepository model to inherit from CenturionModel
ref: #807 #515 #810
2025-07-04 08:40:32 +09:30
Jon
74420f13cc feat(devops): Migrations for switching GitRepository model to inherit from CenturionModel
ref: #807 #515 #809
2025-07-04 08:40:32 +09:30
Jon
192c768f9a feat(devops): Serializers for GitRepository models notes and history
ref: #807 #515 #809
2025-07-04 08:40:32 +09:30
Jon
f6dc4ff512 feat(devops): Serializers for GitHubGitRepository models notes and history
ref: #807 #515 #810
2025-07-04 08:40:32 +09:30
Jon
ec1457f3f8 feat(devops): Serializers for GitLabGitRepository models notes and history
ref: #807 #515 #809
2025-07-04 08:40:32 +09:30
Jon
2ffb93a630 feat(devops): Switch GitLabGitRepository model to inherit from CenturionModel
ref: #807 #515 #809
2025-07-04 08:40:32 +09:30
Jon
4a484c31a2 feat(devops): Switch GitHubGitRepository model to inherit from CenturionModel
ref: #807 #515 #810
2025-07-04 08:40:32 +09:30
Jon
87c5768945 feat(devops): Switch GitRepository model to inherit from CenturionModel
ref: #807 #515 #809
2025-07-04 08:40:32 +09:30
Jon
bff14dc21a refactor(core): adjust CenturionSubModel to not be it's own inheritable class
it's not required as the inheritance does not match the actual class'.

ref: #807 #767
2025-07-04 08:40:32 +09:30
Jon
6b54eebbd2 feat(devops): Update Checkin model url route basename
ref: #807 closes #808
2025-07-04 08:40:32 +09:30
Jon
9a250c988d feat(devops): Add app_namespace Checkin model
ref: #807 #808
2025-07-04 08:40:32 +09:30
Jon
d2d46e7efd test(devops): Add Checkin Unit Model test suite
ref: #807 #676 #808
2025-07-04 08:40:32 +09:30
Jon
2da0fe4e3c feat(devops): Add Checkin to migrate model history/notes
ref: #807 #808
2025-07-04 08:40:32 +09:30
Jon
d43a1bc213 feat(devops): Migrations for switching Checkin model to inherit from CenturionModel
ref: #807 #676 #808
2025-07-04 08:40:32 +09:30
Jon
53726e3eba feat(devops): Switch Checkin model to inherit from CenturionModel
ref: #807 #676 #808
2025-07-04 08:40:32 +09:30
Jon
dff529ba5e test(devops): correct GitGroup Unit model test suite
ref: #807 closes #781
2025-07-04 08:40:31 +09:30
Jon
0d149edee5 test(devops): correct FeatureFlag Unit model test suite
ref: #807 closes #782
2025-07-04 08:40:31 +09:30
Jon
80c1915598 feat(core): add TicketCommentCategory to history/notes migration
ref: #807 closes #798
2025-07-04 08:40:31 +09:30
Jon
32d296885d test(core): Add TicketCommentCategory Unit model test suite
ref: #807 #798
2025-07-04 08:40:31 +09:30
Jon
7480b371aa feat(core): add model tag to ticket comment category
ref: #807 #798
2025-07-04 08:40:31 +09:30
Jon
2d3e496e32 feat(core): Migrations for TicketCategory
ref: #807 closes #797
2025-07-04 08:40:31 +09:30
Jon
5817ff723b feat(core): add TicketCategory to history/notes migration
ref: #807 closes #797
2025-07-04 08:40:31 +09:30
Jon
abdcea07a6 test(core): Add TicketCategory Unit model test suite
ref: #807 #797
2025-07-04 08:40:31 +09:30
Jon
f234b338ee feat(core): add model tag to ticket category
ref: #807 #797
2025-07-04 08:40:31 +09:30
Jon
f2de2dc4ab feat(core): add Manufacturer to history/notes migration
ref: #807 closes #796
2025-07-04 08:40:31 +09:30
Jon
5bc7daf202 test(core): Add Manufacturer Unit model test suite
ref: #807 #796
2025-07-04 08:40:31 +09:30
Jon
ebbb7e8e6e feat(core): add model tag to manufacturer
ref: #807 #796
2025-07-04 08:40:31 +09:30
Jon
14891990f8 chore: add pytest marks to model tests
ref: #807
2025-07-04 08:40:31 +09:30
Jon
48e20a366d feat(config_management): add ConfigGroups to history/notes migration
ref: #807 closes #795
2025-07-04 08:40:31 +09:30
Jon
0c147768ba feat(config_management): add ConfigGroupSoftware to history/notes migration
ref: #807 closes #794
2025-07-04 08:40:31 +09:30
Jon
6025750637 feat(config_management): add ConfigGroupHosts to history/notes migration
ref: #807 closes #793
2025-07-04 08:40:31 +09:30
Jon
b9701757e0 feat(access): add tenant to history/notes migration
ref: #807 closes #790 #785 #786
2025-07-04 08:40:31 +09:30
Jon
d71eee7178 test(access): Add Tenant Unit serializer test suite
ref: #807 #730 #790
2025-07-04 08:40:31 +09:30
Jon
f7c6be71ba test: Add initial unit serializer test suite
ref: #807 #730
2025-07-04 08:40:31 +09:30
Jon
5d09bf04ff chore: Add action to mockview init
ref: #807
2025-07-04 08:40:31 +09:30
Jon
05c9864d68 chore: remove unique field uuid from device kwargs
ref: #805
2025-07-04 08:40:31 +09:30
Jon
1e0539f2d6 fix(access): add property organization to Tenant model
required so that perms work

ref: #805 #790
2025-07-04 08:40:31 +09:30
Jon
f7c581333a chore: Add pytest marks to tests
ref: #805
2025-07-04 08:40:31 +09:30
Jon
ac7af08598 test(access): Update Tenant URL route basename again
ref: #805 #790
2025-07-04 08:40:31 +09:30
Jon
4930523af0 feat(access): Migration for switching model inheritence to CenturionModel
ref: #805 #799
2025-07-04 08:40:31 +09:30
Jon
277c96f3a3 test(itam): Updated Unit model test for Device Model
ref: #805 #799
2025-07-04 08:40:31 +09:30
Jon
aa525407c3 feat(itam): Update model methods
ref: #805 #799
2025-07-04 08:40:31 +09:30
Jon
cf3725f12c chore: update test db fixture
ref: #805
2025-07-04 08:40:31 +09:30
Jon
4740248e7f feat(access): Migration for switching model inheritence to CenturionMixin
ref: #805 #790
2025-07-04 08:40:31 +09:30
Jon
666511b7f4 test(access): Update Tenant URL route basename
ref: #805 #790
2025-07-04 08:40:31 +09:30
Jon
a7a1d85978 test(access): Tenant Model Tests
ref: #805 #790
2025-07-04 08:40:31 +09:30
Jon
35b3710562 chore(access): disable tests to be refactored
ref: #805 #790
2025-07-04 08:40:31 +09:30
Jon
708c2ae92c feat(access): Switch model inheritence to CenturionMixin
ref: #805 #790
2025-07-04 08:40:31 +09:30
Jon
a959e4cbb5 test(api): Update Functional API Permissions to support listview models with kwargs
ref: #805 #767
2025-07-04 08:40:31 +09:30
Jon
0a3f2257e5 chore: update test db fixture
ref: #805
2025-07-04 08:40:31 +09:30
Jon
dac8e41546 test(api): exclude model ConfigGroupHosts from api permission tests as it has no endpoint
ref: #805
2025-07-04 08:40:31 +09:30
Jon
dbdfe4e6c0 refactor(core): Move CenturionModel logic to Mixin
ref: #805 #767
2025-07-04 08:40:31 +09:30
Jon
7ad1851cc0 refactor(core): rename mixin -> mixins
ref: #805 #767
2025-07-04 08:40:31 +09:30
Jon
2bdffb947f chore(base): correct typo
ref: #804
2025-07-04 08:40:31 +09:30
Jon
77c47c12b4 chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #799
2025-07-04 08:40:31 +09:30
Jon
162910d9ec chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-07-04 08:40:31 +09:30
Jon
2eed0bbf4f chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #801
2025-07-04 08:40:31 +09:30
Jon
d97dfa4df1 chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #801
2025-07-04 08:40:31 +09:30
Jon
0f575652f9 chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-07-04 08:40:30 +09:30
Jon
cebc6b668a chore(itam): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-07-04 08:40:30 +09:30
Jon
2fe75ba369 chore(base): correct typo
ref: #804
2025-07-04 08:40:30 +09:30
Jon
2f59a34b69 chore(base): Add pytest marks to pyproject.toml
ref: #804
2025-07-04 08:40:30 +09:30
Jon
613ad7e4e4 refactor(base): model instancxe code de-duplicated
ref: #804
2025-07-04 08:40:30 +09:30
Jon
f5ef97ab4a feat(itam): Update url basename
ref: #804 #794
2025-07-04 08:40:30 +09:30
Jon
a9e0749ad3 feat(itam): Update url basename
ref: #804 #795
2025-07-04 08:40:30 +09:30
Jon
9ed6bcf55c feat(base): add support for manytomany for model unit tests
ref: #804 #799
2025-07-04 08:40:30 +09:30
Jon
aa9a6e09db feat(itam): Update url basename
ref: #804 #799
2025-07-04 08:40:30 +09:30
Jon
5bdabe0793 chore(devops): disable pre CenturionModel inheritance tests
ref: #804 #782
2025-07-04 08:40:30 +09:30
Jon
40a70020a5 revert(access): revert model inheriteance work (db)
ref: #804 #791 #792
2025-07-04 08:40:30 +09:30
Jon
d129256c4e revert(access): revert model inheriteance work
ref: #804 #791
2025-07-04 08:40:30 +09:30
Jon
7753c98f43 revert(access): revert model inheriteance work
ref: #804 #792
2025-07-04 08:40:30 +09:30
Jon
496c5d57ac feat(core): Update url basename
ref: #804 #802
2025-07-04 08:40:30 +09:30
Jon
90fc516722 feat(core): Update url basename
ref: #804 #803
2025-07-04 08:40:30 +09:30
Jon
d1f003820a feat(core): Update url basename
ref: #804 #798
2025-07-04 08:40:30 +09:30
Jon
7a614c80f7 feat(core): Update url basename
ref: #804 #796
2025-07-04 08:40:30 +09:30
Jon
5e0604a44b feat(core): Update url basename
ref: #804 #800
2025-07-04 08:40:30 +09:30
Jon
b5cac23f9d feat(core): Update url basename
ref: #804 #797
2025-07-04 08:40:30 +09:30
Jon
96b83309d1 chore(device): disable pre CenturionModel inheritance tests
ref: #804 #803
2025-07-04 08:40:30 +09:30
Jon
c4c4f8eca5 chore(device): disable pre CenturionModel inheritance tests
ref: #804 #802
2025-07-04 08:40:30 +09:30
Jon
a836e0de47 chore(device): disable pre CenturionModel inheritance tests
ref: #804 #801
2025-07-04 08:40:30 +09:30
Jon
7444b9bacf chore(device): disable pre CenturionModel inheritance tests
ref: #804 #800
2025-07-04 08:40:30 +09:30
Jon
4a9de878ed chore(device): disable pre CenturionModel inheritance tests
ref: #804 #799
2025-07-04 08:40:30 +09:30
Jon
48ea6c4491 chore(core): disable ticket tests for ticket models that are being depreciated
ref: #804 #564
2025-07-04 08:40:30 +09:30
Jon
b3c9f00a08 chore: update fixtures
ref: #804
2025-07-04 08:40:30 +09:30
Jon
8200b47025 fix(itam): Add missing import now
ref: #804 #799
2025-07-04 08:40:30 +09:30
Jon
c70dd867c9 test(api): API Permissions Functional test to supprt name as unique field
ref: #804
2025-07-04 08:40:30 +09:30
Jon
725782af6e feat(access): TeamUsers do not require notes
ref: #804 #792
2025-07-04 08:40:30 +09:30
Jon
2e41922d9c chore: add test fixtures for models
ref: #804 #771
2025-07-04 08:40:30 +09:30
Jon
d3b4958a01 chore: update test db fixture
ref: #804
2025-07-04 08:40:30 +09:30
Jon
7326598f6c fix(core): notes meta model must add model_kwargs fixture
ref: #804 #768
2025-07-04 08:40:30 +09:30
Jon
35b72a048e fix(core): clean_fields for created_by field belongs in model that contains field
ref: #804 #768
2025-07-04 08:40:30 +09:30
Jon
ab68b56056 feat(config_management): ConfigGroupHosts and ConfigGroupSoftware do not require notes
ref: #804 #793 #794
2025-07-04 08:40:30 +09:30
Jon
7b55d37117 fix(core): audit meta model must add model_kwargs fixture
ref: #804
2025-07-04 08:40:30 +09:30
Jon
39d3d0786b fix: model fixture names must match model_name
ref: #804
2025-07-04 08:40:30 +09:30
Jon
41fde61184 chore(core): mock further attributes
ref: #804
2025-07-04 08:40:30 +09:30
Jon
18036ee8d0 chore(config_managment): disable pre CenturionModel inheritance tests
ref: #804 #794
2025-07-04 08:40:30 +09:30
Jon
ec95f4b45d chore(config_managment): disable pre CenturionModel inheritance tests
ref: #804 #795
2025-07-04 08:40:30 +09:30
Jon
520008acff fix: clean up mock model from django apps
ref: #804
2025-07-04 08:40:30 +09:30
Jon
a3b9815891 chore(core): disable pre CenturionModel inheritance tests
ref: #804 #796 #797 #798
2025-07-04 08:40:29 +09:30
Jon
dc56ced765 test(config_management): Completed ConfigGroupSoftware Model Tests
ref: #804 #794
2025-07-04 08:40:29 +09:30
Jon
8c4e5d5c1d test(config_management): Completed ConfigGroup Model Tests
ref: #804 #795
2025-07-04 08:40:29 +09:30
Jon
0e604228aa test(config_management): Completed ConfigGroupHost Model Tests
ref: #804 #793
2025-07-04 08:40:29 +09:30
Jon
4fda640832 chore: for mode instance, actually create it
ref: #804
2025-07-04 08:40:29 +09:30
Jon
9fec7f69f5 chore: add spt to class test cases for fields to be set as non-existant
ref: #804
2025-07-04 08:40:29 +09:30
Jon
71108f6089 refactor(config_management): Add ConfigGroupHost Model Tests
ref: #804 #793
2025-07-04 08:40:29 +09:30
Jon
39208edf93 refactor(config_management): Add ConfigGroupSoftware Model Tests
ref: #804 #794
2025-07-04 08:40:29 +09:30
Jon
2118e56808 refactor(config_management): Add ConfigGroup Model Tests
ref: #804 #795
2025-07-04 08:40:29 +09:30
Jon
d86a719f24 chore(config_management): disable old ConfigGroupSoftware tests
ref: #804 #794
2025-07-04 08:40:29 +09:30
Jon
5c27115991 chore(assistance): disable old ConfigGroup tests
ref: #804 #795
2025-07-04 08:40:29 +09:30
Jon
52235e2e82 chore(assistance): correct fixuture kwargs for kb
ref: #804 #795
2025-07-04 08:40:29 +09:30
Jon
d714476c97 chore(access): remove tests for models that will be depreciated
ref: #804 #791 #792
2025-07-04 08:40:29 +09:30
Jon
274347ebc1 fix(core): When obtaining model fields ensure it exists first
ref: #789 #767
2025-07-04 08:40:29 +09:30
Jon
276e322eef chore(access): TeamUser model does not require audithistory
model will be depreciated soon

ref: #789 #792
2025-07-04 08:40:29 +09:30
Jon
f56ee55347 chore: Update test db fixtures
ref: #789
2025-07-04 08:40:29 +09:30
Jon
ad1f92cdb4 chore(access): Team model Unit Tests for skip
ref: #789 #791
2025-07-04 08:40:29 +09:30
Jon
7cc8192ec2 feat(config_management): Add url_kwargs to ConfigGroupSoftware model
ref: #789 #794
2025-07-04 08:40:29 +09:30
Jon
9509765ba5 feat(access): Add url_kwargs to Team model
ref: #789 #791
2025-07-04 08:40:29 +09:30
Jon
8b694aabf5 feat(access): Add url_kwargs to TeamUser model
ref: #789 #792
2025-07-04 08:40:29 +09:30
Jon
8f0d0fbe16 chore: Remove all references to field is_global
field no longer used

ref: #789 #767
2025-07-04 08:40:29 +09:30
Jon
8c406e1d59 feat(access): Update TeamUser API basename
ref: #789 #792
2025-07-04 08:40:29 +09:30
Jon
9ab4e4ec7e feat(access): Update Team API basename
ref: #789 #791
2025-07-04 08:40:29 +09:30
Jon
2e5d6d4894 fix(access): use getattr instead as attribute may exist as None
ref: #789
2025-07-04 08:40:29 +09:30
Jon
bcd66fd657 feat(itam): switch model Device to inheirt from CenturionModel
ref: #789 #799
2025-07-04 08:40:29 +09:30
Jon
8b8ee525f7 feat(itam): switch model DeviceType to inheirt from CenturionModel
ref: #789 #803
2025-07-04 08:40:29 +09:30
Jon
3e63f896a5 feat(itam): switch model DeviceModel to inheirt from CenturionModel
ref: #789 #800
2025-07-04 08:40:29 +09:30
Jon
7173d9f554 feat(core): switch model TicketCategory to inheirt from CenturionModel
ref: #789 #797
2025-07-04 08:40:29 +09:30
Jon
20b173bf1c feat(core): switch model TicktetCommentCategory to inheirt from CenturionModel
ref: #789 #798
2025-07-04 08:40:29 +09:30
Jon
ac6f580153 feat(core): switch model Manufacturer to inheirt from CenturionModel
ref: #789 #796
2025-07-04 08:40:29 +09:30
Jon
d06bf78d0a fix(assistance): make kb article field longer for model name
ref: #789
2025-07-04 08:40:29 +09:30
Jon
e75bc124c9 feat(config_management): switch model ConfigGroupHosts to inheirt from CenturionModel
ref: #789 #795
2025-07-04 08:40:29 +09:30
Jon
fc60d7f3be feat(config_management): switch model ConfigGroupSoftware to inheirt from CenturionModel
ref: #789 #795
2025-07-04 08:40:29 +09:30
Jon
cff3bc5b2c feat(config_management): switch model ConfigGroups to inheirt from CenturionModel
ref: #789 #795
2025-07-04 08:40:29 +09:30
Jon
8202a37576 feat(access): switch model TeamUsers to inheirt from CenturionModel
ref: #789 #792
2025-07-04 08:40:29 +09:30
Jon
5186b2f173 feat(access): switch model Team to inheirt from CenturionModel
ref: #789 #791
2025-07-04 08:40:29 +09:30
Jon
e9a8ad1c48 test(core): mock the user object within the model context
ref: #789 #759
2025-07-04 08:40:28 +09:30
Jon
143dd2af35 chore(access): remove old ui mixin
ref: #789 #757
2025-07-04 08:40:28 +09:30
Jon
09beeb46af test(core): creating a model is a functional not unit test
ref: #789 #759
2025-07-04 08:40:28 +09:30
Jon
883c0961f6 chore: as part of api func tests, dont delete api perms user as part of fixture cleanup
ref: #789
2025-07-04 08:40:28 +09:30
Jon
3d9cde3b25 chore: Add support for confftest for parameterized tests to have no values
ref: #789
2025-07-04 08:40:28 +09:30
Jon
1d80cdd5c3 docs: update release notes
ref: #789
2025-07-04 08:40:28 +09:30
Jon
86da111b85 chore(project_management): Remove API v1 code
ref: #789 #343
2025-07-04 08:40:28 +09:30
Jon
38b1746f26 chore(itam): Remove API v1 code
ref: #789 #343
2025-07-04 08:40:28 +09:30
Jon
f6e3390d26 chore(core): Remove API v1 code
ref: #789 #343
2025-07-04 08:40:28 +09:30
Jon
cf8feb2637 chore(config_management): Remove API v1 code
ref: #789 #343
2025-07-04 08:40:28 +09:30
Jon
f2a2352a3c chore(base): Remove API v1 code
ref: #789 #343
2025-07-04 08:40:28 +09:30
Jon
78334da031 chore(api): Remove API v1 code
ref: #789 #343
2025-07-04 08:40:28 +09:30
Jon
398b78855d chore(access): Remove API v1 code
ref: #789 #343
2025-07-04 08:40:28 +09:30
Jon
7691ad5b0b feat(core): If user context not supplied, dont create audithistory for model
this allows tests and direct creation via code if required.

ref: #789 #759
2025-07-04 08:40:28 +09:30
Jon
a8e2536bd2 chore(base): remove tests no longer used
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
dd9a906073 chore(base): remove methods no longer used in context processor
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
82bfacbab3 chore: disable api field notess and history tests
tests being re-written

ref: #788 #759 #768
2025-07-04 08:40:28 +09:30
Jon
728db3f740 chore: Add premeta notes and history models to init so they can be migrated
ref: #788 #759 #768
2025-07-04 08:40:28 +09:30
Jon
8a390b9caf chore(core): remove old old history model
ref: #788 #759
2025-07-04 08:40:28 +09:30
Jon
3155794931 chore(core): Remove Template tags
not using django ui anymore

ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
f79a9d628b chore(api): Remove pre-meta history code from common serializer
ref: #788 #759
2025-07-04 08:40:28 +09:30
Jon
bf39631db8 chore(settings): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
3c114abb11 chore(project_management): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
a30ba02def chore(itim): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
aa7de88f8d chore(itam): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
24c960dc86 chore(devops): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
0ded2bcebd chore(core): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
82f0f87878 chore(config_management): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
0792dd1604 chore(api): Remove pre-meta notes code from common serializer
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
baa9a6206d chore(accounting): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
bf7179b4f9 chore(access): Remove pre-meta notes code
ref: #788 #768
2025-07-04 08:40:28 +09:30
Jon
b8db09d9b8 chore: temp disable api field notes and history checks
ref: #788
2025-07-04 08:40:28 +09:30
Jon
9ff93976f2 chore(base): pass instance of model for class tests
ref: #788
2025-07-04 08:40:28 +09:30
Jon
b535aa4703 chore(project_management): disable project test that contain kb untill rewrite
ref: #788 #735
2025-07-04 08:40:28 +09:30
Jon
074aa4832a feat(access): Add init to tenancy model to clear state
ref: #788 #767
2025-07-04 08:40:28 +09:30
Jon
cc483e2cb5 chore(core): disable ticket linked item kb article functional test
ref: #788 #735
2025-07-04 08:40:28 +09:30
Jon
f8faa7f040 chore(settings): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
764e631be6 chore(project_management): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
882c2afc9f chore(itim): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
6aa7953c2a chore(itam): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
25d510f086 chore(core): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
97b07e1663 chore(config_management): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
3d961ecc9e chore(base): disable tests that require rewrite
ref: #788 #735
2025-07-04 08:40:28 +09:30
Jon
05921057fd chore(assistance): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
90f68d7fe5 chore(api): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:28 +09:30
Jon
642ecc4e6c chore(access): Remove no longer used Django UI
ref: #788 #757
2025-07-04 08:40:28 +09:30
Jon
b1530b3c19 chore(base): Dont reload model, reset the parts that change in tests
creates issues with mocking

ref: #788
2025-07-04 08:40:28 +09:30
Jon
c64add9ce7 chore(core): disable old delete so it's no longer used by models yet to inherit from CenturionModel
ref: #788 #759
2025-07-04 08:40:28 +09:30
Jon
3915678675 feat(core): Ensure that model has user context
ref: #788 #759
2025-07-04 08:40:27 +09:30
Jon
f42e255f37 chore(core): disable old delete so it's no longer used by models yet to inherit from CenturionModel
ref: #788 #759
2025-07-04 08:40:27 +09:30
Jon
c0455af2fa chore(itam): mock base delete so abstract classes can be called
ref: #788
2025-07-04 08:40:27 +09:30
Jon
4d211a2cb7 chore(assistance): disable tests that require rewrite
ref: #788 #735
2025-07-04 08:40:27 +09:30
Jon
3205f9e946 chore(base): add fixture try block
ref: #788
2025-07-04 08:40:27 +09:30
Jon
0c62172f03 chore(base): fixture sanitization
ref: #783
2025-07-04 08:40:27 +09:30
Jon
c8a5bbcfb6 chore(itam): Remove more old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
cf5f43f33c chore(settings): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
3f05100cde chore(project_management): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
b6bc39e784 chore(itim): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
d4350dc444 chore(itam): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
c3905360f4 chore(core): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
71e2d2f112 chore(config_management): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
d0f3923226 chore(access): Remove old Notes Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
a53f4caa81 chore(settings): fixture sanitization
ref: #783
2025-07-04 08:40:27 +09:30
Jon
d18c9006d2 chore(settings): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
388f9a6039 chore(project_management): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
0a1166f1df chore(itim): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
74e54343b9 chore(itam): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
2cec1be952 chore(human_resources): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
b21b0f0b5b chore(core): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
731df89b40 chore(config_management): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
072cda66bf chore(accounting): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
d3f367f4b9 chore(access): Remove old History Tests
ref: #783 #735 #759
2025-07-04 08:40:27 +09:30
Jon
391a2f7767 feat(core): Add supprt to model_instance fixture for manytomany field
ref: #783
2025-07-04 08:40:27 +09:30
Jon
40ffdd86ba fix(assistance): Add missing field model_notes to KB serializer
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
223aae2eb4 chore(assistance): Remove old KB notes
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
22e35fbf1c chore(assistance): Remove old KB Category notes
ref: #783 #786
2025-07-04 08:40:27 +09:30
Jon
ba6f09c9b5 chore(base): Update test db creation fixtures
ref: #783
2025-07-04 08:40:27 +09:30
Jon
2509743597 refactor(assistance): Refactor KnowledgeBaseCategory Unit model tests
ref: #783 #786 #735
2025-07-04 08:40:27 +09:30
Jon
b76bc5a416 refactor(assistance): Update KnowledgeBase Unit viewset url basename
ref: #783 #785 #735
2025-07-04 08:40:27 +09:30
Jon
a6d781be47 refactor(assistance): Refactor KnowledgeBase Unit model tests
ref: #783 #785 #735
2025-07-04 08:40:27 +09:30
Jon
69bbe94b06 feat(core): Add supprt to model create test for manytomany field
ref: #783
2025-07-04 08:40:27 +09:30
Jon
be3ce40471 chore(assistance): Remove KnowledgeBase Note ViewSet
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
98fd342343 chore(assistance): Remove KnowledgeBaseCategory Note ViewSet
ref: #783 #786
2025-07-04 08:40:27 +09:30
Jon
e0a4526d0c chore(assistance): TMP disable KB category tests that require refactor
ref: #783 #786
2025-07-04 08:40:27 +09:30
Jon
2b6d421b91 chore(assistance): TMP disable KB tests that require refactor
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
a3fda45055 feat(assistance): migrations for new history and notes models for KnowledgeBaseCategory model
ref: #783 #786
2025-07-04 08:40:27 +09:30
Jon
0a0577aa24 feat(assistance): migrations for new history and notes models for KnowledgeBase model
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
78ed699463 feat(assistance): Model inheritance migrations
ref: #783 #785 #786
2025-07-04 08:40:27 +09:30
Jon
6d0f655c97 chore(assistance): remove old history and notes tests for KnowledgeBaseCategory model
ref: #783 #786
2025-07-04 08:40:27 +09:30
Jon
2220dcf851 chore(assistance): remove old history and notes tests for KnowledgeBase model
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
a5a391fba6 refactor(assistance): Add new history and notes Serializer for KnowledgeBase model
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
de9358c943 refactor(assistance): Add new history and notes Serializer for KnowledgeBaseCategory model
ref: #783 #786
2025-07-04 08:40:27 +09:30
Jon
f5b1738fd2 refactor(assistance): Change KnowledgeBaseCategory model inheritance TenancyObject -> CenturionModel
ref: #783 #786
2025-07-04 08:40:27 +09:30
Jon
b80cc3e839 refactor(assistance): Change KnowledgeBase model inheritance TenancyObject -> CenturionModel
ref: #783 #785
2025-07-04 08:40:27 +09:30
Jon
734c29f41b refactor(assistance): MV kb category model to its own file
ref: #783
2025-07-04 08:40:27 +09:30
Jon
507182983b chore(devops): Remove GitGroup old notes URL route
ref: #783 #781
2025-07-04 08:40:26 +09:30
Jon
0a413a6581 chore(devops): Remove GitGroup old notes serializer
ref: #783 #781
2025-07-04 08:40:26 +09:30
Jon
c476f2a12c chore(devops): Remove GitGroup old notes viewset
ref: #783 #781
2025-07-04 08:40:26 +09:30
Jon
0f3da21aa4 chore(devops): Add FeatureFlag old history and notes models to init so they exist
ref: #783 #782
2025-07-04 08:40:26 +09:30
Jon
d5b465ea0e chore(devops): Add GitGroup old history and notes models to init so they exist
ref: #783 #781
2025-07-04 08:40:26 +09:30
Jon
caeabe0793 feat(core): Migrate Centurion Model history and notes within a post_migrate signal
ref: #783 #758 #759 #778 #768
2025-07-04 08:40:26 +09:30
Jon
9dc2114258 fix(core): Before attempting to get model audit data confirm fields dont already exist
ref: #783
2025-07-04 08:40:26 +09:30
Jon
91b33be834 fix(api): check if model has notes enabled before adding url to body
ref:
2025-07-04 08:40:26 +09:30
Jon
6f66b4cf95 test(devops): re-implement temp removed test suites.
ref: #780 #515 #782
2025-07-04 08:40:26 +09:30
Jon
0abb416620 test(api): API Permissions Auto-Creator test suite
ref: #780 #730 #767
2025-07-04 08:40:26 +09:30
Jon
3a010e166e test(devops): Add GitGroup API Permissions tests
ref: #780 #515
2025-07-04 08:40:26 +09:30
Jon
616f37fca2 test(core): Add fixtures for api permission tests
ref: #780 #730
2025-07-04 08:40:26 +09:30
Jon
c042725dc7 test(core): rewrite api permissions test suite to use pytest and fixtures
ref: #780 #730
2025-07-04 08:40:26 +09:30
Jon
026c408754 feat(core): Add ability to CenturionModel get_url to be either detail/list
ref: #780 #767
2025-07-04 08:40:26 +09:30
Jon
ae42147f69 fix(api): Only return View Serialized data if status code is HTTP/2xx
ref: #780
2025-07-04 08:40:26 +09:30
Jon
764ae1133d chore: update db test fixture
ref: #780
2025-07-04 08:40:26 +09:30
Jon
1d31a259df refactor(tests): Create global model fixtures
ref: #780 #729
2025-07-04 08:40:26 +09:30
Jon
66d14ea5bb chore(tests): Depreciate old FeateFlag Tests
ref: #780 #729
2025-07-04 08:40:26 +09:30
Jon
2f22d0f135 docs(development): Update test suit locations
ref: #780 #729
2025-07-04 08:40:26 +09:30
Jon
56bb1863f2 chore: move api render to functional dir
ref: #780 #729
2025-07-04 08:40:26 +09:30
Jon
9790ee51f3 chore(devops): Remove FeatureFlag Notes Tests
ref: #780 #782
2025-07-04 08:40:26 +09:30
Jon
8892ba7e3f chore(devops): Remove FeatureFlag History Tests
ref: #780 #782
2025-07-04 08:40:26 +09:30
Jon
9e93c12860 chore(devops): Remove FeatureFlag notes URLRoute
ref: #780 #782
2025-07-04 08:40:26 +09:30
Jon
cf3c177715 chore(devops): Remove FeatureFlag notes ViewSet
ref: #780 #782
2025-07-04 08:40:26 +09:30
Jon
db93903119 chore(devops): Remove FeatureFlag notes Serializer
ref: #780 #782
2025-07-04 08:40:26 +09:30
Jon
4414f585a6 feat(core): New Management command to list models
ref: #780 #771
2025-07-04 08:40:26 +09:30
Jon
f6634bab78 refactor(devops): Switch FeatureFlag model unit tests to CenturionModel
ref: #780 #782
2025-07-04 08:40:26 +09:30
Jon
ef294cc081 feat(devops): Switch model FeatureFlag inheritance to CenturionModel
ref: #780 #782
2025-07-04 08:40:26 +09:30
Jon
2d1ec6a84a docs: Update model notes docs for new meta model
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
b37a284a0a test(core): Ensure Method clean_fields functions for CenturionNotesModel
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
1fa3278b73 test(core): Function Model test suite for CenturionModelNote Meta Models
ref: #779 #778 #768
2025-07-04 08:40:26 +09:30
Jon
5bf9c30c58 chore: Update test fixtures
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
637a76f1fe test(core): Interim Unit Model test suite for CenturionModelNote Meta Models
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
9afca66ad6 test(core): Interim Unit Model test suite for CenturionModelNote
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
031fd79f13 feat(core): Disable Notes for model CenturionModelNote
A notes model does not require notes

ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
5429c17f98 feat(devops): Enable Model notes for GitGroup
ref: #779 #778 #515
2025-07-04 08:40:26 +09:30
Jon
473ffb0ec0 feat(core): add Swagger docs for CenturionModelNotes ViewSet
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
62e7eb9c47 feat(core): Meta Model for CenturionModelNotes
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
695bfa7f51 feat(core): Finalize Serializer for CenturionModelNotes
ref: #779 #778
2025-07-04 08:40:26 +09:30
Jon
a5227f0608 feat(api): Add to common serializer meta notes model for notes url
ref: #779 #767
2025-07-04 08:40:26 +09:30
Jon
1d77247e40 feat(core): Interim Meta model CenturionNotes
ref: #776 #778
2025-07-04 08:40:26 +09:30
Jon
bb774bc2c9 feat(core): Interim ViewSet for model CenturionNotes
ref: #776 #778
2025-07-04 08:40:26 +09:30
Jon
ce96d71083 feat(core): URL Route for model CenturionNotes
ref: #776 #778
2025-07-04 08:40:26 +09:30
Jon
c905f8c010 feat(core): Serializer for model CenturionNotes
ref: #776 #778
2025-07-04 08:40:26 +09:30
Jon
084bdf61bd feat(core): Migration for model CenturionNotes
ref: #776 #778
2025-07-04 08:40:26 +09:30
Jon
21b3aa3b4a feat(core): Add model CenturionNotes
ref: #776 #778
2025-07-04 08:40:26 +09:30
Jon
8c6e863403 chore: correct liniting errors
ref: #776
2025-07-04 08:40:26 +09:30
Jon
f890fb6d52 feat(devops): dont allow deleting a git group if it has children
ref: #776
2025-07-04 08:40:25 +09:30
Jon
0babe48208 chore: correct liniting errors
ref: #776
2025-07-04 08:40:25 +09:30
Jon
4290136b69 chore: update pylint to v3.3.7
ref: #776
2025-07-04 08:40:25 +09:30
Jon
121a0c3f2c refactor(settings): move url routes from core.urls to own module urls_api.py
ref: #776 closes #777
2025-07-04 08:40:25 +09:30
Jon
e04869cc65 refactor(project_management): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-07-04 08:40:25 +09:30
Jon
dc6d34de71 refactor(itim): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-07-04 08:40:25 +09:30
Jon
0b7fcbbc26 refactor(itam): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-07-04 08:40:25 +09:30
Jon
1cc378c69a refactor(core): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-07-04 08:40:25 +09:30
Jon
959b8ac51a refactor(config_management): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-07-04 08:40:25 +09:30
Jon
f4f9729c7d refactor(assistance): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-07-04 08:40:25 +09:30
Jon
54195c271a refactor(access): move url routes from core.urls to own module urls_api.py
ref: #776 #777
2025-07-04 08:40:25 +09:30
Jon
79b8cdd26b chore: add fixture to setup user perms for api tests
ref: #776 #730 #735
2025-07-04 08:40:25 +09:30
Jon
f53531fbbd docs(development): Audit History API Docs added
ref: #776 #759
2025-07-04 08:40:25 +09:30
Jon
6141ad1d5a docs(development): Audit History updates
ref: #776 #759
2025-07-04 08:40:25 +09:30
Jon
428770a8eb chore: rm pylint error
this file is due for refactor so urls are in own modules

ref: #776
2025-07-04 08:40:25 +09:30
Jon
11e12ec70f chore: code review fixes
ref: #775
2025-07-04 08:40:25 +09:30
Jon
ba64fdd36e chore: add further pylint rules
ref: #775
2025-07-04 08:40:25 +09:30
Jon
416b6f5a75 test(core): Dynamic Unit Test Suites for Meta Models AuditHistory
ref: #775 #759 #767
2025-07-04 08:40:25 +09:30
Jon
adc1c8fba7 test(core): Unit Test Centurion Model method __str__
ref: #767
2025-07-04 08:40:25 +09:30
Jon
ade35d0e2d test(core): Unit Test Centurion Model method get_url_kwargs
ref: #775 #759 #767
2025-07-04 08:40:25 +09:30
Jon
212af84d28 test(core): Unite Tesxt Centurion Model method get_url attr _is_submodel set
ref: #775 #759 #767
2025-07-04 08:40:25 +09:30
Jon
7ed75fc3e9 test(core): Unite Tesxt Centurion Model method get_url attr model_name set
ref: #775 #767
2025-07-04 08:40:25 +09:30
Jon
e554923c0b feat(devops): Add model tag attribute to model
ref: #773 #515 #576
2025-07-04 08:40:25 +09:30
Jon
17f4295763 feat(core): Add to Centurion Model an attribute to set the models tag
ref: #773 #767 #576
2025-07-04 08:40:25 +09:30
Jon
8e81bc06db docs(user): Add AuditHsitory Docs
ref: #773 #759
2025-07-04 08:40:25 +09:30
Jon
0b30f3b70f feat(core): Add Context to model when ViewSet loads
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
827894ef23 feat(devops): Add AuditHistory Serializer for GitGroup
ref: #773 #759
2025-07-04 08:40:25 +09:30
Jon
7a2e77cba6 feat(core): Add AuditHistory Serializer
ref: #773 #759
2025-07-04 08:40:25 +09:30
Jon
187ad724a2 feat(core): Add AuditHistory ViewSet
ref: #773 #759
2025-07-04 08:40:25 +09:30
Jon
9e2587d6c3 feat(core): Add URL route for AuditHistory
ref: #773 #759
2025-07-04 08:40:25 +09:30
Jon
21e7d7b444 feat(core): Add audithistory URL to serializer for models with _audit_enabled=True
ref: #773 #767 #759
2025-07-04 08:40:25 +09:30
Jon
26b5aa7f45 docs(development): Initial Serializer docs
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
37ff9d38cb fix(core): Conduct kwargs check fr ticket comment serializer during init
ref: #773
2025-07-04 08:40:25 +09:30
Jon
f440788dae refactor(api): Update Common ViewSet methds for re-write
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
279ea1991d feat(core): Models url kwarg helper
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
6b899e477d feat(core): Support setting custom model name for url basename
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
9a3e17a6d3 feat(api): Add sub-model filter to get_queryset method
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
851d6e8b2f chore: correct test so it works
ref: #773 #759
2025-07-04 08:40:25 +09:30
Jon
114b2e1ae3 test(core): Ensure model that has audit enableed has audit model
ref: #773 #515
2025-07-04 08:40:25 +09:30
Jon
2b6a15cf8f test(core): Add Functional model Test Suite for CenturionAuditModel
ref: #773 #515
2025-07-04 08:40:25 +09:30
Jon
a46187f36e test(devops): Ensure that a Github group cant have a parent/"be nested"
ref: #773 #515 #249
2025-07-04 08:40:25 +09:30
Jon
fc256445db test(devops): Ensure that when create a child git group that the tenancy matches the parent git group
ref: #773 #515
2025-07-04 08:40:25 +09:30
Jon
60f6269a71 test(devops): Add Functional model Test Suite
ref: #773 #515
2025-07-04 08:40:25 +09:30
Jon
660da485cd test(core): Add Base Centurion model Functional Test Suite
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
d86b07b4fa test(access): Add Base Tenancy model Functional Test Suite
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
ea7cea2aa9 test(base): Add Base model Functional Test Suite
ref: #773 #767
2025-07-04 08:40:25 +09:30
Jon
06e8e277c1 feat(core): Disable models audit history on model delete
can't create an audit entry without a model, why? it was deleted

ref: #773 #759
2025-07-04 08:40:25 +09:30
Jon
df4ffa5e93 fix(core): Enable CenturionAudit model to get model history for item being deleted
ref: #773 #759
2025-07-04 08:40:24 +09:30
Jon
15ab6a7f70 chore: update test fixtures
ref: #773
2025-07-04 08:40:24 +09:30
Jon
8e0906eb73 test(core): Model Unit Tests for AuditHistory get_model_history method
ref: #773 #759
2025-07-04 08:40:24 +09:30
Jon
685de22ad2 chore: add pytest marks to tests
ref: #772
2025-07-04 08:40:24 +09:30
Jon
62ec58d9d8 test(core): reset vals so as not to fuck other tests over
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
ee79dafd6b test(core): Correct test for method get_audit_values for CenturionAbstractModel
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
d31b70a2a0 chore(core): update Current ModelHistory query to exclude new AuditHistory model
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
766b4272be chore(devops): remove tests that were left there when file was copied from another test suite
ref: #772 #515
2025-07-04 08:40:24 +09:30
Jon
76daab8e1f chore: adjust test so params are under a property
ref: #772
2025-07-04 08:40:24 +09:30
Jon
82e617afbe fix(core): When creating the AuditHistory entry for a model, use the user from context
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
197cf4eb73 fix(core): When collecting AuditHistory cater for models being created
ref: #772 #767
2025-07-04 08:40:24 +09:30
Jon
5a80e48b21 feat(core): Use Previous TenancyManager until UserModel rewrite done
ref: #772 #766 #767
2025-07-04 08:40:24 +09:30
Jon
bd66b8ec94 chore: remove Adding of history tasks from issue_template for new models
ref: #772
2025-07-04 08:40:24 +09:30
Jon
d8b69e3ff2 fix(api): remove surerflous feature for fetching app_namespace for models metadata
if the model required the parent models `app_namespace` it already exists when the class is inerited,

ref: #772
2025-07-04 08:40:24 +09:30
Jon
cff519d199 test(devops): Initial Model Unit tests for GitGroup
ref: #772 #515
2025-07-04 08:40:24 +09:30
Jon
2e88dfc5c1 refactor(devops): Switch GitGroup Model to CenturionModel
ref: #772 #515 #759 #767
2025-07-04 08:40:24 +09:30
Jon
f5a2168958 feat(core): Process a models history within AuditHistory
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
35a46c90d3 feat(core): Enable AuditHistory signal to start when apps are ready
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
bc1f12c47f feat(core): Add model instance to history object during history creation
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
7a1e9e10a3 feat(core): Update Meta AuditModel db_name to be suffixed _audithistory
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
9425793190 fix(core): Correct attribute names for referencing a Centurion Model from an AuditModel
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
d3347082d1 test(core): Add field model_notes as an excluded field for AuditModels
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
12ec0f34f9 feat(core): remove unnessecary method clean_fields from audit model
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
5e169eb7f8 chore(core): Add temp objects to CenturionAbstractModel to support depreciated features
these will be removed when #767 is complete

ref: #772 #767
2025-07-04 08:40:24 +09:30
Jon
105d89ee61 fix(core): Correct before lookup for current models audit history
ref: #772 #759 #767
2025-07-04 08:40:24 +09:30
Jon
64e53f6980 feat(core): remove un-needed field model_notes from audit models
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
0c6a15d22d docs(development): Add new abstract models to API docs
ref: #772
2025-07-04 08:40:24 +09:30
Jon
db41c73300 test(core): Remaining Unit Model Test Cases for CenturionAuditMeta Model
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
da3a471d04 docs(development): Partial update to models
ref: #772 #767
2025-07-04 08:40:24 +09:30
Jon
b5147e2448 refactor(core): Loading of meta models should not be hidden behind program start ags
Meta Models must always exist

ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
0a35a32136 test(core): Initial Unit Model Test Cases for CenturionAuditMeta Model
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
8b62014d88 test(core): Unit Model Test Cases for CenturionSubAbstract model
ref: #772 #767
2025-07-04 08:40:24 +09:30
Jon
16bbb3328b test(core): Initial Unit Model Test Cases for CenturionAudit Model
ref: #772 #759
2025-07-04 08:40:24 +09:30
Jon
d0ce759635 test(core): Unit test cases for Centurion get_url relative + non-relative
ref: #772 #767
2025-07-04 08:40:24 +09:30
Jon
6f0ef8ab31 test(access): Unit Model Tests for TenancyAbstractModel
ref: #770 #767 #445
2025-07-04 08:40:24 +09:30
Jon
28117bb2bb chore(core): remove methods not required
ref: #770 #767
2025-07-04 08:40:24 +09:30
Jon
4d93ba7339 refactor(core): To obtain audit_values loop through model fields
ref: #770 #767
2025-07-04 08:40:24 +09:30
Jon
d098a09e83 fix(core): When deleting a model check if sub-model within delete method
ref: #770 #767
2025-07-04 08:40:24 +09:30
Jon
678bc18cb6 test(base): Unit Common Model test cases suite
ref: #770 #758 #767
2025-07-04 08:40:24 +09:30
Jon
8fd418787e test(base): Unit Common Class test cases suite
ref: #770 #758 #767
2025-07-04 08:40:24 +09:30
Jon
85d8d74cbf test(access): Unit Model Tests for TenancyAbstractModel
ref: #770 #758 #767
2025-07-04 08:40:24 +09:30
Jon
3e6d9233ca chore(pytest): rename pytest_configure -> pytest_report_header
ref: #770
2025-07-04 08:40:24 +09:30
Jon
06ad1fec5e chore(pytest): Create and use fixture of Fresh DB instead of migrations for testing
ref: #770
2025-07-04 08:40:24 +09:30
Jon
95281d35eb chore(pytest): give parameterized tests the option to use functions as a value
ref: #770
2025-07-04 08:40:24 +09:30
Jon
5aae562caf feat(core): Run meta models create on Core module ready
ref: #770 #759
2025-07-04 08:40:24 +09:30
Jon
49e66df006 feat(core): New model core.CenturionAudit
ref: #770 #759
2025-07-04 08:40:24 +09:30
Jon
504ded9047 feat(core): cause sub-audit models to chuck a wobbler if clean_fields not re-implementated
ref: #770 #759
2025-07-04 08:40:24 +09:30
Jon
a7f84924db chore: remove unused imports
ref: #770
2025-07-04 08:40:24 +09:30
Jon
6647ddbc99 chore: comment out test setup for skeleton test suites not in use yet
ref: #770
2025-07-04 08:40:24 +09:30
Jon
73fdaee33f refactor: rejig whats in each inherited centurion model
ref: #770
2025-07-04 08:40:24 +09:30
Jon
0884bc314a chore: add linting rule for unused imports
ref: #770
2025-07-04 08:40:23 +09:30
Jon
15b4446775 fix(access): Tenancy Manager should not attempt to get org as related field if it does not exist
ref: #770
2025-07-04 08:40:23 +09:30
Jon
af2bc77920 feat(access): remove mill-seconds from datetime auto fields
ref: #770 #759
2025-07-04 08:40:23 +09:30
Jon
9cb507286f chore: correct code from revert
ref: #765
2025-07-04 08:40:23 +09:30
Jon
db97b574da feat(core): Centurion model Base
ref: #765 #767
2025-07-04 08:40:23 +09:30
Jon
31e200bb01 feat(core): Centurion Audit model
ref: #765 #759
2025-07-04 08:40:23 +09:30
Jon
0192f2f3a8 fix(api): ensure val returns at least none
ref: #765
2025-07-04 08:40:23 +09:30
Jon
8f097fb44e chore(access): planning work for centurion Tenancy model
ref: #765 #766
2025-07-04 08:40:23 +09:30
Jon
daf2811be7 chore(access): planning work for centurion user model
ref: #765 #766
2025-07-04 08:40:23 +09:30
Jon
2c33fa6f62 revert(core): Relocate history model class
reverted commit was 5cc08e3e94

ref: #765 #766
2025-07-04 08:40:23 +09:30
Jon
9d7a3e2e79 refactor(access): prefetch org with tenancy object
ref: #765
2025-07-04 08:40:23 +09:30
Jon
23dfdfe0b3 chore(core): remove old manual migration no longer required
ref: #765
2025-07-04 08:40:23 +09:30
Jon
473763af36 feat(core): permissions getter for role model
ref: #765 #551
2025-07-04 08:40:23 +09:30
Jon
47c12f5d55 chore(core): skeleton test cases
ref: #765 #759
2025-07-04 08:40:23 +09:30
Jon
64ea376962 feat(core): Audit History Signal for Delete/Save
ref: #765 #759
2025-07-04 08:40:23 +09:30
Jon
bd7ff92ab3 chore: Add pylint conf
ref: #765
2025-07-04 08:40:23 +09:30
Jon
bff1049414 chore: correct pylint errors
ref: #765
2025-07-04 08:40:23 +09:30
Jon
a1b9ecb0fc feat(core): Dynamic History model creation
ref: #765 #758 #759
2025-07-04 08:38:55 +09:30
Jon
262e883a26 refactor(core): Relocate history model class
ref: #765 #759
2025-07-04 08:38:55 +09:30
Jon
17c7980e03 refactor(base): rename app to centurion
ref: #764
2025-07-04 08:38:55 +09:30
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
1788 changed files with 62045 additions and 67261 deletions

View File

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

View File

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

7
.gitignore vendored
View File

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

View File

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

60
.vscode/launch.json vendored
View File

@ -4,7 +4,6 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Centurion",
"type": "debugpy",
@ -18,7 +17,6 @@
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Debug: Gunicorn",
"type": "debugpy",
"request": "launch",
@ -31,8 +29,7 @@
"3",
"--bind",
"0.0.0.0:8002",
"app.wsgi:application",
"centurion.wsgi:application",
],
"django": true,
"autoStartBrowser": false,
@ -53,7 +50,29 @@
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Centurion Model (Management Command)",
"type": "debugpy",
"request": "launch",
"args": [
"models",
// "0.0.0.0:8002"
],
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Make Migrations",
"type": "debugpy",
"request": "launch",
"args": [
"makemigrations"
],
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Migrate",
"type": "debugpy",
@ -64,7 +83,6 @@
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/app/manage.py"
},
{
"name": "Debug: Celery",
@ -82,6 +100,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": "."
}
]
},
]
}

16
.vscode/settings.json vendored
View File

@ -5,19 +5,25 @@
"!python"
],
"python.testing.pytestArgs": [
// "-v",
// "--cov",
// "--cov-report xml",
"-s",
"--override-ini", "addopts=",
"--no-migrations",
"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,
"python.languageServer": "None",
}

View File

@ -1,3 +1,305 @@
## 1.18.0 (2025-07-03)
### feat
- **python**: upgrade django 5.1.9 -> 5.1.10
### Fixes
- **itim**: Correct config that is in the incorrect format
## 1.17.1 (2025-06-02)
### Fixes
- **base**: Add python metrics to prometheus exporter
## 1.17.0 (2025-05-16)
### feat
- **access**: model access.Company feature flag `2025-00008`
- **access**: URL route for model access.Company
- **access**: Migration for model access.Company
- **access**: Serializer for model access.Company
- **access**: New model access.Company
- **access**: Organization -> Tenant Permission Migration
- **docker**: Serve a robots.txt file for NO indexing
- **access**: Organization -> Tenant Permission Migration
- **base**: Add var `AUTH_USER_MODEL` to settings
- **core**: Add Action comments on ticket change
- **core**: Remove add, change and delete permissions for model TicketCommentAction from permission selector
- **core**: Serializer for model TicketCommentAction
- **core**: Migrations for model TicketCommentAction
- **core**: New model TicketCommentAction
- **core**: Setup serializer to meet requirements
- **core**: Setup model to meet requirements
- **api**: Add exception logging to ViewSetCommon
- **python**: Upgrade DRF Spectacular 0.27.2 -> 0.28.0
- **python**: Upgrade DRF 3.15.2 -> 3.16.0
- **core**: When processing slash command duration, cater for new ticket models
- **api**: Add Logging function to Common ViewSet
- **access**: Add Logging function to Tenancy model
- **base**: Enable user to customize log file location
- **core**: Do validate the comment_type field for TicketCommentBase
- **itam**: Add Feature Flag `2025-00007` ITAMAssetBase
- **itam**: Add endpoint for ITAMAssetBase
- Model tag migration for Asset and IT Asset
- **itam**: Model tag for ITAsset
- **accounting**: Model tag for Asset
- **accounting**: Add app label to kb articles for notes
- **accounting**: Migrations for notes model for AssetBase
- **accounting**: Migrations for history model for AssetBase
- **accounting**: Notes Viewset for AssetBase
- **accounting**: Notes Serializer for AssetBase
- **accounting**: Notes model for AssetBase
- **accounting**: History model for AssetBase
- **itam**: Serializer for ITAssetBase
- **itam**: Migrations for ITAssetBase
- **itam**: Add Model ITAssetBase
- **accounting**: Viewset for Assets
- **accounting**: Serializer for model AssetBase
- **accounting**: Migrations for model AssetBase
- **accounting**: Add Model AssetBase
### Fixes
- **api**: Dont try to access attribute if not exist in common viewset
- **api**: Dont try to access attribute if not exist in common viewset
- **api**: Correct ViewSet Sub-Model lookup
- **core**: Only take action on ticket comment if view exists
- **api**: Ensure multi-nested searching for sub-models works
- **core**: ensure slash command is called on ticket description
- **core**: Spent slash command is valid for time spent
- **core**: Correct logic for TicketCommentSolution
- **core**: Correct logic for TicketCommentBase
- **accounting**: Ensure correct sub-model check is conducted within model type
- **itam**: ensure RO field asset_type is set
- **itim**: Ensure that itam base model is always imported
### Refactoring
- **human_resources**: Update Functional ViewSet to use PyTest for Employee Model
- **Access**: Update Functional ViewSet to use PyTest for Person Model
- **Access**: Update Functional ViewSet to use PyTest for Entity Model
- **Access**: Update Functional ViewSet to use PyTest for Contact Model
- **Access**: Update Functional Permission to use PyTest for Person Model
- **Access**: Update Functional Permission to use PyTest for Entity Model
- **Access**: Update Functional Permission to use PyTest for Contact Model
- **Access**: Update Functional Serializer to use PyTest for Contact Model
- **Access**: Update Functional Serializer to use PyTest for Entity Model
- **Access**: Update Functional Serializer to use PyTest for Person Model
- **human_resources**: Update Functional Serializer to use PyTest for Employee Model
- **human_resources**: Update Functional Permissions to use PyTest for Employee Model
- **human_resources**: Update Functional Metadata to use PyTest for Employee Model
- **access**: Update Functional Metadata to use PyTest for Person Model
- **access**: Update Functional Metadata to use PyTest for Entity Model
- **access**: Update Functional Metadata to use PyTest for Contact Model
- **access**: Update Model Entity to use PyTest for Model Test Suite
- **access**: Update Model Contact to use PyTest for Model Test Suite
- **access**: Update Model Person to use PyTest for Model Test Suite
- **human_resources**: Update Model Employee to use PyTest for Model Test Suite
- **human_resources**: Update Model Employee to use PyTest API Fields Render
- **access**: Update Model Person to use PyTest API Fields Render
- **access**: Update Model Contact to use PyTest API Fields Render
- **access**: Update Model Entity to use PyTest API Fields Render
- **access**: Rename model Organization -> Tenant
- **settings**: Update all references to `User` to use `get_user_model()`
- **project_management**: Update all references to `User` to use `get_user_model()`
- **itam**: Update all references to `User` to use `get_user_model()`
- **devops**: Update all references to `User` to use `get_user_model()`
- **core**: Update all references to `User` to use `get_user_model()`
- **config_management**: Update all references to `User` to use `get_user_model()`
- **assistance**: Update all references to `User` to use `get_user_model()`
- **app**: Update all references to `User` to use `get_user_model()`
- **api**: Update all references to `User` to use `get_user_model()`
- **accounting**: Update all references to `User` to use `get_user_model()`
- **access**: Update all references to `User` to use `get_user_model()`
- **access**: when fetching parent object, use the parent_model get function
- **api**: Limit url pk regex to ensure the value is a number
### Tests
- **access**: Functional ViewSet Test Suite Company model
- **access**: Functional Serializer Test Suite Company model
- **access**: Functional Permissions Test Suite Company model
- **access**: Functional MetaData Test Suite Company model
- **access**: ViewSet Test Suite Company model
- **access**: API field render Test Suite Company model
- **access**: Model Test Suite Company model
- **core**: Unit viewset Test Cases for TicketCommentAction model
- **core**: Unit model Test Cases for TicketCommentAction model
- **core**: Unit API Render Test Cases for TicketCommentAction model
- **core**: Interim Functional model Test Case TicketCommentAction
- **core**: Ensure that a ticket milestone comes from the same assigned project
- **core**: SKIP Tests TicketBase Description Slash command Checks
- **core**: TicketBase Description Slash command Checks
- **core**: TicketBase Remaining Serializer Chacks
- **core**: Partial functional Model Test Suite covering some slash commande for TicketCommentSolution
- **core**: ensure ticket is un-solved for ticketcomment unit api render fields check
- **core**: ensure slash command is called on ticket comment
- **core**: Unit ViewSet Test Suite for TicketCommentSolution
- **core**: Unit ViewSet Test Suite for TicketCommentBase
- **core**: Skip Related slash command checks until migrating tickets to new model
- **core**: Add ability to unit api field rendering test case for second api request if required
- **core**: Partial Functional Model test cases (Slash Commands) for TicketCommentBase
- **core**: Functional Model test cases (Slash Commands) for TicketBaseModel
- **core**: Partial Slash Command re-write
- **core**: correct field so its valid for unit TicketCommentBase model
- **core**: Unit API Fields Render for TicketCommentSolution model
- **core**: Unit API Fields Render for TicketCommentBase model
- **core**: Unit Model assert save and call are called for TicketBase
- **core**: Unit Model Checks for TicketCommentSolution
- **core**: Unit Model Checks for TicketCommentBase
- **itam**: test meta attribute itam_sub_model_type for ITAMBaseModel
- **itam**: Dont use constants where variables should be used
- **itam**: Remaining Unit Model test cases for AssetBase
- **accounting**: Remaining Unit Model test cases for AssetBase
- **itam**: Functional ViewSet Test Cases for ITAMAssetBase
- **itam**: Functional Serializer Test Cases for ITAMAssetBase
- **itam**: Functional Permissions Test Cases for ITAMAssetBase
- **itam**: Functional Metadata Test Cases for ITAMAssetBase
- **itam**: Functional History Test Cases for ITAMAssetBase
- **accounting**: Functional ViewSet Test Cases for AssetBase
- **accounting**: Functional Serializer Test Cases for AssetBase
- **accounting**: Functional Permissions Test Cases for AssetBase
- **accounting**: Functional Metadata Test Cases for AssetBase
- **accounting**: History Test Cases for AssetBase
- add missing merge of add_data for api permissions tests
- remove ticket only vars from api permissions tests
- **api**: dont use constants for variable data
- correct viewset tests
- **itam**: Unit Viewset checks for AssetBase Model
- **core**: Add missing fields is_global checks for ticket base
- **api**: Add submodel url resolution for metadata
- **itam**: Unit API Fields checks for ITAM AssetBase Model
- **accounting**: Unit API Fields checks for AssetBase Model
- Support variables that were defined as properties.
- **api**: Ensure that model notes is added to model create for api field tests
- **accounting**: Unit Viewset checks for AssetBase Model
- **itam**: Unit Model checks for ITAMAssetBase Model
- **base**: update Model base test suite for model_notes field
- **accounting**: Unit Model checks for AssetBase Model
## 1.16.0 (2025-05-04)
### feat
- **core**: Add ViewSet for Ticket Comments
- **project_management**: Depreciate Project Task Ticket Endpoint
- **itim**: Depreciate Problem Ticket Endpoint
- **itim**: Depreciate Incident Ticket Endpoint
- **itim**: Depreciate Change Ticket Endpoint
- **assistance**: Depreciate Ticket Comment
- **assistance**: Depreciate Request Ticket Endpoint
- **core**: Add routes for Ticket Comments
- **core**: update ticket serializer to use new comment base url
- **core**: Add permissions `import`, `purge` and `triage` to model TicketCommentSolution
- **core**: Add permissions `import`, `purge` and `triage` to model TicketCommentBase
- **core**: Filter ticket_comment_model routes to those defined in `Meta.sub_model_type`
- **core**: Filter ticket_model routes to those defined in `Meta.sub_model_type`
- **access**: Filter entity_model routes to thos defined in `Meta.sub_model_type`
- **core**: Serializer for TicketCommentBase
- **core**: Serializer for TicketCommentSolution
- **core**: Ticket Comment Get URL functions
- **core**: Ticket Comment Validation for comment_type
- **core**: Update choices fields for TicketCommentBase model
- **core**: init for model TicketCommentSolution
- **core**: Migrations for choice fields for TicketBase model
- **core**: Migrations for model TicketCommentSolution
- **core**: Update choice fields for TicketBase model
- **core**: New model TicketCommentSolution
- **api**: when fetching related_object, default to base_model for SubModelViewSet
- Add field `Meta.sub_model_type` to sub-models
- **core**: New interim model TicketCommentSolution
- **core**: add ticket routes
- **itim**: serializer for SLMTicketBase
- **itim**: Serializer for RequestTicket
- **itim**: migrations for RequestTicket
- **itim**: New Model RequestTicket
- **itim**: migration for SLMTicketBase
- **itim**: New Model SLMTicketBase
- **core**: migrations for TicketCommentBase
- **core**: New Model TicketCommentBase
- **core**: viewset for TicketBase
- **core**: serializer for TicketBase
- **core**: migrations for TicketBase
- **core**: New Model TicketBase
- **project_management**: add estimation field to project api fields
- **human_resources**: nav menu entries for Employee
- **human_resources**: Serializer for Employee
- **human_resources**: Migration for Employee
- **human_resources**: New model Employee
- **devops**: add missing api index menu entry for devops
- **access**: add missing nav menu entries for entities
- **human_resources**: add module to perms selector
### Fixes
- **test**: correct typo in attribute parameterized_
- **core**: Ticktet comment can have empty body
- **core**: If model does not save history, dont attempt to cache before
- **itam**: provide return_url as part of software version meta
- **itim**: correct ticket_slm serializer
- **itim**: correct ticket_request serializer
- **api**: SubModelViewSet.related_objects must be the same class as the base model
- **access**: Ensure related model is a sub-model
- **human_resources**: Correct history link generation and add docs
- **human_resources**: Correct history link generation
- **access**: add missing attribute to Tenancy object
### Refactoring
- **test**: rewrite model unit tests to use PyTest
- **test**: Update test parameterization
- **api**: SubModelViewSet must inherit from ModelViewSet as it's an extension
- **core**: rename ticket model filename in preparation for base ticket model
- **access**: migrate sub-model viewset logic to common
- **project_management**: add duration field to project api fields
- **human_resources**: Move employee details to its own section
### Tests
- **core**: Serializer Validation for ticket status change for TicketBase model
- **core**: Prevent Closing / Solving of TicketBase Model if not ready
- **itim**: Incomplete Model Unit Tests for RequestTicket
- **itim**: Incomplete Model Unit Tests for SLMTicketBase
- **core**: Incomplete Model Unit Tests for TicketBase
- **itim**: RequestTicket Updated, yet incomplete Test Suite for Serializer
- **itim**: SLMTicketBase Updated, yet incomplete Test Suite for Serializer
- **core**: TicketBase Updated, yet incomplete Test Suite for Serializer
- Correct Test Suite for Serializer for models TicketBase, TicketRequest and TicketSLM
- **itim**: RequestTicket Initial Test Suite for Serializer
- **itim**: SLMTicket Initial Test Suite for Serializer
- **core**: TicketBase Initial Test Suite for Serializer
- **core**: SLMTicket Test Suite for ViewSet
- **core**: SLMTicket Test Suite for Metadata
- **core**: Request Test Suite for ViewSet
- **core**: Request Test Suite for Metadata
- **core**: TicketBase Test Suite for ViewSet
- **core**: TicketBase Test Suite for Metadata
- **api**: update test cases for SubModelViewSet Base Test Class
- **itim**: RequestTicket ViewSet Test Suite
- **core**: TicketBase ViewSet Test Suite
- **api**: Incomplete SubModelViewSet Test Cases
- **api**: SubModelViewSet Test Suite Setup
- correct tests from Meta.sub_model_type changes
- correct serializer imports from recent file renames
- Fixture for creating model with random data
- **itim**: API Field checks for TicketSLMBase
- **itim**: API Field checks for TicketRequest
- **core**: API fields Tests for TicketBase
- **core**: API fields Unit Test Suite
- **core**: Correct model notes test suite
- **core**: API Permission Test Cases for ticket_base model
- **api**: add API Permission Test Cases
- **access**: Correct history link test cases
- **project_management**: Add test cases for api field render for model fields `estimation_project` and `duration_project`
- **human_resources**: History Serializer and ViewSet Functional test suites for employee
- **human_resources**: APIv2, History, Model and ViewSet Unit test suites for employee
- Migrate models to use refactored model tests
- Consolidate All model tests to remove duplicates and to simplify
## 1.15.1 (2025-04-10)
### Fixes

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,10 @@
from django.contrib.auth.models import Permission
from django.apps import apps
from django.contrib.auth.models import (
ContentType,
Permission
)
from django.conf import settings
def permission_queryset():
"""Filter Permissions to those used within the application
@ -7,13 +13,15 @@ def permission_queryset():
list: Filtered queryset that only contains the used permissions
"""
apps = [
centurion_apps = [
'access',
'accounting',
'assistance',
'config_management',
'core',
'devops',
'django_celery_results',
'human_resources',
'itam',
'itim',
'project_management',
@ -35,22 +43,65 @@ def permission_queryset():
'add_history',
'add_organization',
'add_taskresult',
'add_ticketcommentaction',
'change_checkin',
'change_history',
'change_organization',
'change_taskresult',
'change_ticketcommentaction',
'delete_checkin',
'delete_history',
'delete_organization',
'delete_taskresult',
'delete_ticketcommentaction',
'view_checkin',
'view_history',
]
if not settings.RUNNING_TESTS:
models = apps.get_models()
for model in models:
if(
not str(model._meta.object_name).endswith('AuditHistory')
and not str(model._meta.model_name).lower().endswith('history')
):
# check `endswith('history')` can be removed when the old history models are removed
continue
content_type = ContentType.objects.get(
app_label = model._meta.app_label,
model = model._meta.model_name
)
permissions = Permission.objects.filter(
content_type = content_type,
)
for permission in permissions:
if(
not permission.codename == 'view_' + str(model._meta.model_name)
and str(model._meta.object_name).endswith('AuditHistory')
):
exclude_permissions += [ permission.codename ]
elif(
not str(model._meta.object_name).endswith('AuditHistory')
and str(model._meta.model_name).lower().endswith('history')
):
# This `elif` can be removed when the old history models are removed
exclude_permissions += [ permission.codename ]
return Permission.objects.select_related('content_type').filter(
content_type__app_label__in=apps,
content_type__app_label__in = centurion_apps,
).exclude(
content_type__model__in=exclude_models
content_type__model__in = exclude_models
).exclude(
codename__in = exclude_permissions
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,110 @@
# Generated by Django 5.1.9 on 2025-06-06 01:41
import access.models.tenancy_abstract
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0010_company_alter_entity_entity_type_alter_person_dob_and_more"),
("core", "0028_delete_history"),
]
operations = [
migrations.RemoveField(
model_name="team",
name="is_global",
),
migrations.AlterField(
model_name="team",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="team",
name="organization",
field=models.ForeignKey(
help_text="Tenant this belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.tenant",
validators=[
access.models.tenancy_abstract.TenancyAbstractModel.validatate_organization_exists
],
verbose_name="Tenant",
),
),
migrations.CreateModel(
name="TeamAuditHistory",
fields=[
(
"centurionaudit_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionaudit",
),
),
(
"model",
models.ForeignKey(
help_text="Model this history belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="audit_history",
to="access.team",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team History",
"verbose_name_plural": "Team Histories",
"db_table": "access_team_audithistory",
"managed": True,
},
bases=("core.centurionaudit", models.Model),
),
migrations.CreateModel(
name="TeamCenturionModelNote",
fields=[
(
"centurionmodelnote_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionmodelnote",
),
),
(
"model",
models.ForeignKey(
help_text="Model this note belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.team",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team Note",
"verbose_name_plural": "Team Notes",
"db_table": "access_team_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote", models.Model),
)
]

View File

@ -0,0 +1,102 @@
# Generated by Django 5.1.9 on 2025-06-06 01:43
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0011_remove_team_is_global_model_notes_and_more"),
("core", "0028_delete_history"),
]
operations = [
migrations.AddField(
model_name="teamusers",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="teamusers",
name="id",
field=models.AutoField(
help_text="ID of the item",
primary_key=True,
serialize=False,
unique=True,
verbose_name="ID",
),
),
migrations.CreateModel(
name="TeamUsersAuditHistory",
fields=[
(
"centurionaudit_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionaudit",
),
),
(
"model",
models.ForeignKey(
help_text="Model this history belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="audit_history",
to="access.teamusers",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team User History",
"verbose_name_plural": "Team User Histories",
"db_table": "access_teamusers_audithistory",
"managed": True,
},
bases=("core.centurionaudit", models.Model),
),
migrations.CreateModel(
name="TeamUsersCenturionModelNote",
fields=[
(
"centurionmodelnote_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionmodelnote",
),
),
(
"model",
models.ForeignKey(
help_text="Model this note belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.teamusers",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Team User Note",
"verbose_name_plural": "Team User Notes",
"db_table": "access_teamusers_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote", models.Model),
),
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,253 @@
# Generated by Django 5.1.9 on 2025-06-17 07:32
import access.models.tenancy_abstract
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0017_remove_entitynotes_model_and_more"),
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
]
operations = [
migrations.RemoveField(
model_name="entity",
name="is_global",
),
migrations.AlterField(
model_name="entity",
name="id",
field=models.AutoField(
help_text="ID of the item",
primary_key=True,
serialize=False,
unique=True,
verbose_name="ID",
),
),
migrations.AlterField(
model_name="entity",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="entity",
name="organization",
field=models.ForeignKey(
help_text="Tenant this belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.tenant",
validators=[
access.models.tenancy_abstract.TenancyAbstractModel.validatate_organization_exists
],
verbose_name="Tenant",
),
),
migrations.CreateModel(
name="ContactAuditHistory",
fields=[
(
"centurionaudit_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionaudit",
),
),
(
"model",
models.ForeignKey(
help_text="Model this history belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.contact",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Contact History",
"verbose_name_plural": "Contact Histories",
"db_table": "access_contact_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="ContactCenturionModelNote",
fields=[
(
"centurionmodelnote_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionmodelnote",
),
),
(
"model",
models.ForeignKey(
help_text="Model this note belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.contact",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Contact Note",
"verbose_name_plural": "Contact Notes",
"db_table": "access_contact_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
migrations.CreateModel(
name="EntityAuditHistory",
fields=[
(
"centurionaudit_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionaudit",
),
),
(
"model",
models.ForeignKey(
help_text="Model this history belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="audit_history",
to="access.entity",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Entity History",
"verbose_name_plural": "Entity Histories",
"db_table": "access_entity_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="EntityCenturionModelNote",
fields=[
(
"centurionmodelnote_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionmodelnote",
),
),
(
"model",
models.ForeignKey(
help_text="Model this note belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.entity",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Entity Note",
"verbose_name_plural": "Entity Notes",
"db_table": "access_entity_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
migrations.CreateModel(
name="PersonAuditHistory",
fields=[
(
"centurionaudit_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionaudit",
),
),
(
"model",
models.ForeignKey(
help_text="Model this history belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.person",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Person History",
"verbose_name_plural": "Person Histories",
"db_table": "access_person_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="PersonCenturionModelNote",
fields=[
(
"centurionmodelnote_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionmodelnote",
),
),
(
"model",
models.ForeignKey(
help_text="Model this note belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.person",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Person Note",
"verbose_name_plural": "Person Notes",
"db_table": "access_person_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
]

View File

@ -0,0 +1,81 @@
# Generated by Django 5.1.10 on 2025-07-06 10:38
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0018_remove_entity_is_global_alter_entity_id_and_more"),
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
]
operations = [
migrations.CreateModel(
name="CompanyAuditHistory",
fields=[
(
"centurionaudit_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionaudit",
),
),
(
"model",
models.ForeignKey(
help_text="Model this history belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.company",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Company History",
"verbose_name_plural": "Company Histories",
"db_table": "access_company_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="CompanyCenturionModelNote",
fields=[
(
"centurionmodelnote_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionmodelnote",
),
),
(
"model",
models.ForeignKey(
help_text="Model this note belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.company",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Company Note",
"verbose_name_plural": "Company Notes",
"db_table": "access_company_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
]

View File

@ -0,0 +1,56 @@
# Generated by Django 5.1.10 on 2025-07-12 07:20
import access.models.tenancy_abstract
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0019_companyaudithistory_companycenturionmodelnote"),
]
operations = [
migrations.AlterField(
model_name="role",
name="id",
field=models.AutoField(
help_text="ID of the item",
primary_key=True,
serialize=False,
unique=True,
verbose_name="ID",
),
),
migrations.AlterField(
model_name="role",
name="model_notes",
field=models.TextField(
blank=True,
help_text="Tid bits of information",
null=True,
verbose_name="Notes",
),
),
migrations.AlterField(
model_name="role",
name="organization",
field=models.ForeignKey(
help_text="Tenant this belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.tenant",
validators=[
access.models.tenancy_abstract.TenancyAbstractModel.validatate_organization_exists
],
verbose_name="Tenant",
),
),
migrations.DeleteModel(
name="RoleHistory",
),
migrations.DeleteModel(
name="RoleNotes",
),
]

View File

@ -0,0 +1,81 @@
# Generated by Django 5.1.10 on 2025-07-12 08:50
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("access", "0020_remove_rolenotes_model_and_more"),
("core", "0033_alter_ticketcommentcategory_parent_and_more"),
]
operations = [
migrations.CreateModel(
name="RoleAuditHistory",
fields=[
(
"centurionaudit_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionaudit",
),
),
(
"model",
models.ForeignKey(
help_text="Model this history belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="audit_history",
to="access.role",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Role History",
"verbose_name_plural": "Role Histories",
"db_table": "access_role_audithistory",
"managed": True,
},
bases=("core.centurionaudit",),
),
migrations.CreateModel(
name="RoleCenturionModelNote",
fields=[
(
"centurionmodelnote_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="core.centurionmodelnote",
),
),
(
"model",
models.ForeignKey(
help_text="Model this note belongs to",
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="access.role",
verbose_name="Model",
),
),
],
options={
"verbose_name": "Role Note",
"verbose_name_plural": "Role Notes",
"db_table": "access_role_centurionmodelnote",
"managed": True,
},
bases=("core.centurionmodelnote",),
),
]

View File

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

View File

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

View File

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

View File

@ -1,2 +1,3 @@
from . import contact
from . import person
from .organization_history import OrganizationHistory # pylint: disable=W0611:unused-import
from .organization_notes import OrganizationNotes # pylint: disable=W0611:unused-import

View File

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

View File

@ -8,6 +8,10 @@ class Contact(
Person
):
documentation = ''
_is_submodel = True
class Meta:
@ -15,6 +19,8 @@ class Contact(
'email',
]
sub_model_type = 'contact'
verbose_name = 'Contact'
verbose_name_plural = 'Contacts'
@ -40,8 +46,6 @@ class Contact(
return self.f_name + ' ' + self.l_name
documentation = ''
page_layout: list = [
{
"name": "Details",

View File

@ -1,18 +1,23 @@
from django.db import models
from rest_framework.reverse import reverse
from access.fields import AutoLastModifiedField
from access.fields import AutoCreatedField, AutoLastModifiedField
from access.models.tenancy import TenancyObject
from core.lib.feature_not_used import FeatureNotUsed
from core.models.centurion import CenturionModel
class Entity(
TenancyObject
CenturionModel
):
model_tag = 'entity'
documentation = ''
kb_model_name = 'entity'
url_model_name = 'entity'
class Meta:
@ -22,36 +27,27 @@ class Entity(
'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,
default = Meta.verbose_name.lower(),
help_text = 'Type this entity is',
max_length = 30,
unique = False,
verbose_name = 'Entity Type'
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
def __str__(self) -> str:
related_model = self.get_related_model()
if related_model is None:
@ -62,22 +58,8 @@ class Entity(
return str( related_model )
# app_namespace = 'access'
history_app_label = 'access'
history_model_name = 'entity'
kb_model_name = 'entity'
note_basename = '_api_v2_entity_note'
documentation = ''
page_layout: dict = []
table_fields: list = [
'organization',
'entity_type',
@ -87,21 +69,41 @@ class Entity(
]
def clean_fields(self, exclude = None ):
related_model = self.get_related_model()
if related_model is None:
related_model = self
if self.entity_type != str(related_model._meta.verbose_name).lower().replace(' ', '_'):
self.entity_type = str(related_model._meta.verbose_name).lower().replace(' ', '_')
super().clean_fields( exclude = exclude )
def get_related_field_name(self) -> str:
meta = getattr(self, '_meta')
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(
if(
not str(related_object.name).endswith('history')
and not str(related_object.name).endswith('notes')
):
return related_object.name
break
return ''
@ -125,117 +127,17 @@ class Entity(
if related_model_name == '':
return None
related_model = None
elif related_model is None:
return self
related_model = self
elif related_model.get_related_field_name() != '':
elif hasattr(related_model, 'get_related_field_name'):
related_model = related_model.get_related_model()
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

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

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

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,20 @@
from django.contrib.auth.models import Permission
from django.db import models
from access.fields import AutoCreatedField, AutoLastModifiedField
from access.models.tenancy import TenancyObject
from access.fields import AutoLastModifiedField
from core.models.centurion import CenturionModel
class Role(
TenancyObject
CenturionModel
):
documentation = ''
model_tag = 'role'
class Meta:
@ -28,14 +33,6 @@ class Role(
verbose_name_plural = 'Roles'
id = models.AutoField(
blank=False,
help_text = 'Primary key of the entry',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
name = models.CharField(
blank = False,
help_text = 'Name of this role',
@ -53,21 +50,15 @@ class Role(
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",
@ -130,14 +121,29 @@ class Role(
]
def save_history(self, before: dict, after: dict) -> bool:
_permissions: list[ Permission ] = None
from access.models.role_history import RoleHistory
_permissions_int: list[ int ] = None
history = super().save_history(
before = before,
after = after,
history_model = RoleHistory
)
def get_permissions(self, as_int_list = False ):
return history
if self._permissions is None:
permissions = []
permissions_int = []
for permission in self.permissions: # pylint: disable=E1133:not-an-iterable
if permission in _permissions:
continue
permissions += [ permission ]
permissions_int += [ permission.id ]
self._permissions = permissions
self._permissions_int = permissions_int
if as_int_list:
return self._permissions_int
return self._permissions_int

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

@ -0,0 +1,56 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from api.serializers import common
from centurion.models.meta import CompanyAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionaudit import (
BaseSerializer,
ViewSerializer as AuditHistoryViewSerializer
)
@extend_schema_serializer(component_name = 'CompanyAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = CompanyAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'CompanyAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View File

@ -0,0 +1,56 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from api.serializers import common
from centurion.models.meta import ContactAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionaudit import (
BaseSerializer,
ViewSerializer as AuditHistoryViewSerializer
)
@extend_schema_serializer(component_name = 'ContactAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = ContactAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'ContactAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View File

@ -0,0 +1,56 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from api.serializers import common
from centurion.models.meta import EntityAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionaudit import (
BaseSerializer,
ViewSerializer as AuditHistoryViewSerializer
)
@extend_schema_serializer(component_name = 'EntityAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = EntityAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'EntityAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View File

@ -0,0 +1,56 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from api.serializers import common
from centurion.models.meta import PersonAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionaudit import (
BaseSerializer,
ViewSerializer as AuditHistoryViewSerializer
)
@extend_schema_serializer(component_name = 'PersonAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = PersonAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'PersonAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View File

@ -0,0 +1,56 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from api.serializers import common
from centurion.models.meta import RoleAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionaudit import (
BaseSerializer,
ViewSerializer as AuditHistoryViewSerializer
)
@extend_schema_serializer(component_name = 'RoleAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = RoleAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'RoleAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View File

@ -0,0 +1,56 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from api.serializers import common
from centurion.models.meta import TenantAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionaudit import (
BaseSerializer,
ViewSerializer as AuditHistoryViewSerializer
)
@extend_schema_serializer(component_name = 'TenantAuditHistoryModelSerializer')
class ModelSerializer(
common.CommonModelSerializer,
BaseSerializer
):
"""Git Group Audit History Base Model"""
_urls = serializers.SerializerMethodField('get_url')
class Meta:
model = TenantAuditHistory
fields = [
'id',
'organization',
'display_name',
'content_type',
'model',
'before',
'after',
'action',
'user',
'created',
'_urls',
]
read_only_fields = fields
@extend_schema_serializer(component_name = 'TenantAuditHistoryViewSerializer')
class ViewSerializer(
ModelSerializer,
AuditHistoryViewSerializer,
):
"""Git Group Audit History Base View Model"""
pass

View File

@ -0,0 +1,87 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from access.serializers.organization import (TenantBaseSerializer)
from centurion.models.meta import CompanyCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
BaseSerializer,
ModelSerializer as BaseModelModelSerializer,
ViewSerializer as BaseModelViewSerializer
)
@extend_schema_serializer(component_name = 'CompanyModelNoteModelSerializer')
class ModelSerializer(
BaseModelModelSerializer,
):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
}
class Meta:
model = CompanyCenturionModelNote
fields = [
'id',
'organization',
'display_name',
'body',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'organization',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
def validate(self, attrs):
is_valid = False
note_model = self.Meta.model.model.field.related_model
attrs['model'] = note_model.objects.get(
id = int( self.context['view'].kwargs['model_id'] )
)
is_valid = super().validate(attrs)
return is_valid
@extend_schema_serializer(component_name = 'CompanyModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View File

@ -0,0 +1,87 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from access.serializers.organization import (TenantBaseSerializer)
from centurion.models.meta import ContactCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
BaseSerializer,
ModelSerializer as BaseModelModelSerializer,
ViewSerializer as BaseModelViewSerializer
)
@extend_schema_serializer(component_name = 'ContactModelNoteModelSerializer')
class ModelSerializer(
BaseModelModelSerializer,
):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
}
class Meta:
model = ContactCenturionModelNote
fields = [
'id',
'organization',
'display_name',
'body',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'organization',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
def validate(self, attrs):
is_valid = False
note_model = self.Meta.model.model.field.related_model
attrs['model'] = note_model.objects.get(
id = int( self.context['view'].kwargs['model_id'] )
)
is_valid = super().validate(attrs)
return is_valid
@extend_schema_serializer(component_name = 'ContactModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View File

@ -0,0 +1,87 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from access.serializers.organization import (TenantBaseSerializer)
from centurion.models.meta import EntityCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
BaseSerializer,
ModelSerializer as BaseModelModelSerializer,
ViewSerializer as BaseModelViewSerializer
)
@extend_schema_serializer(component_name = 'EntityModelNoteModelSerializer')
class ModelSerializer(
BaseModelModelSerializer,
):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
}
class Meta:
model = EntityCenturionModelNote
fields = [
'id',
'organization',
'display_name',
'body',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'organization',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
def validate(self, attrs):
is_valid = False
note_model = self.Meta.model.model.field.related_model
attrs['model'] = note_model.objects.get(
id = int( self.context['view'].kwargs['model_id'] )
)
is_valid = super().validate(attrs)
return is_valid
@extend_schema_serializer(component_name = 'EntityModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View File

@ -0,0 +1,87 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from access.serializers.organization import (TenantBaseSerializer)
from centurion.models.meta import PersonCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
BaseSerializer,
ModelSerializer as BaseModelModelSerializer,
ViewSerializer as BaseModelViewSerializer
)
@extend_schema_serializer(component_name = 'PersonModelNoteModelSerializer')
class ModelSerializer(
BaseModelModelSerializer,
):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
}
class Meta:
model = PersonCenturionModelNote
fields = [
'id',
'organization',
'display_name',
'body',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'organization',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
def validate(self, attrs):
is_valid = False
note_model = self.Meta.model.model.field.related_model
attrs['model'] = note_model.objects.get(
id = int( self.context['view'].kwargs['model_id'] )
)
is_valid = super().validate(attrs)
return is_valid
@extend_schema_serializer(component_name = 'PersonModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View File

@ -0,0 +1,87 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from access.serializers.organization import (TenantBaseSerializer)
from centurion.models.meta import RoleCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
BaseSerializer,
ModelSerializer as BaseModelModelSerializer,
ViewSerializer as BaseModelViewSerializer
)
@extend_schema_serializer(component_name = 'RoleModelNoteModelSerializer')
class ModelSerializer(
BaseModelModelSerializer,
):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
}
class Meta:
model = RoleCenturionModelNote
fields = [
'id',
'organization',
'display_name',
'body',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'organization',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
def validate(self, attrs):
is_valid = False
note_model = self.Meta.model.model.field.related_model
attrs['model'] = note_model.objects.get(
id = int( self.context['view'].kwargs['model_id'] )
)
is_valid = super().validate(attrs)
return is_valid
@extend_schema_serializer(component_name = 'RoleModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View File

@ -0,0 +1,87 @@
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_serializer
from access.serializers.organization import (TenantBaseSerializer)
from centurion.models.meta import TenantCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module
from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import
BaseSerializer,
ModelSerializer as BaseModelModelSerializer,
ViewSerializer as BaseModelViewSerializer
)
@extend_schema_serializer(component_name = 'TeamModelNoteModelSerializer')
class ModelSerializer(
BaseModelModelSerializer,
):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
}
class Meta:
model = TenantCenturionModelNote
fields = [
'id',
'organization',
'display_name',
'body',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'organization',
'created_by',
'modified_by',
'content_type',
'model',
'created',
'modified',
'_urls',
]
def validate(self, attrs):
is_valid = False
note_model = self.Meta.model.model.field.related_model
attrs['model'] = note_model.objects.get(
id = int( self.context['view'].kwargs['model_id'] )
)
is_valid = super().validate(attrs)
return is_valid
@extend_schema_serializer(component_name = 'TeamModelNoteViewSerializer')
class ViewSerializer(
ModelSerializer,
BaseModelViewSerializer,
):
organization = TenantBaseSerializer( many = False, read_only = True )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,101 +0,0 @@
import pytest
import unittest
from access.models.tenancy import TenancyManager
class TenancyObject:
""" Tests for checking TenancyObject """
model = None
""" Model to be tested """
should_model_history_be_saved: bool = True
""" Should model history be saved.
By default this should always be 'True', however in special
circumstances, this may not be desired.
"""
def test_history_save(self):
"""Confirm the desired intent for saving model history."""
assert self.model.save_model_history == self.should_model_history_be_saved
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')
def test_has_attr_model_notes(self):
""" TenancyObject attribute check
TenancyObject has field model_notes
"""
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,204 @@
import pytest
import random
from django.test import Client
class AdditionalTestCases:
def test_permission_add(self, model_instance, api_request_permissions,
model_kwargs, kwargs_api_create
):
""" Check correct permission for add
Attempt to add as user with permission
"""
client = Client()
client.force_login( api_request_permissions['user']['add'] )
the_model = model_instance( kwargs_create = self.kwargs_create_item )
url = the_model.get_url( many = True )
the_model.delete()
kwargs = kwargs_api_create.copy()
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
response = client.post(
path = url,
data = kwargs,
content_type = 'application/json'
)
assert response.status_code == 201, response.content
def test_returned_results_only_user_orgs(self, model_instance, model_kwargs, api_request_permissions):
"""Returned results check
Ensure that a query to the viewset endpoint does not return
items that are not part of the users organizations.
"""
if model_kwargs.get('organization', None) is None:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
client = Client()
viewable_organizations = [
api_request_permissions['tenancy']['user'].id,
]
if getattr(self, 'global_organization', None):
# Cater for above test that also has global org
viewable_organizations += [ api_request_permissions['tenancy']['global'] ]
client.force_login( api_request_permissions['user']['view'] )
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['different']
})
model_instance(
kwargs_create = kwargs
)
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['global']
})
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
model_instance(
kwargs_create = kwargs
)
kwargs = self.kwargs_create_item.copy()
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
the_model = model_instance( kwargs_create = kwargs )
response = client.get(
path = the_model.get_url( many = True )
)
# if response.status_code == 405:
# pytest.xfail( reason = 'ViewSet does not have this request method.' )
# elif IsAuthenticatedOrReadOnly in response.renderer_context['view'].permission_classes:
# pytest.xfail( reason = 'ViewSet is public viewable, test is N/A' )
assert response.status_code == 200
contains_different_org: bool = False
for item in response.data['results']:
if 'organization' not in item:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
if(
int(item['organization']['id']) not in viewable_organizations
and
int(item['organization']['id']) != api_request_permissions['tenancy']['global'].id
):
contains_different_org = True
print(f'Failed returned row was: {item}')
assert not contains_different_org
def test_returned_data_from_user_and_global_organizations_only(
self, model_instance, model_kwargs, api_request_permissions
):
"""Check items returned
Items returned from the query Must be from the users organization and
global ONLY!
"""
if model_kwargs.get('organization', None) is None:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
client = Client()
only_from_user_org: bool = True
viewable_organizations = [
api_request_permissions['tenancy']['user'].id,
api_request_permissions['tenancy']['global'].id
]
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['different']
})
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
the_model = model_instance(
kwargs_create = kwargs
)
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['global']
})
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
model_instance(
kwargs_create = kwargs
)
client.force_login( api_request_permissions['user']['view'] )
kwargs = self.kwargs_create_item.copy()
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
the_model = model_instance( kwargs_create = kwargs )
response = client.get(
path = the_model.get_url( many = True )
)
assert len(response.data['results']) >= 2 # fail if only one item extist.
for row in response.data['results']:
if 'organization' not in row:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
if row['organization']['id'] not in viewable_organizations:
only_from_user_org = False
print(f"Users org: {api_request_permissions['tenancy']['user'].id}")
print(f"global org: {api_request_permissions['tenancy']['global'].id}")
print(f'Failed returned row was: {row}')
assert only_from_user_org

View File

@ -0,0 +1,204 @@
import pytest
import random
from django.test import Client
class AdditionalTestCases:
def test_permission_add(self, model_instance, api_request_permissions,
model_kwargs, kwargs_api_create
):
""" Check correct permission for add
Attempt to add as user with permission
"""
client = Client()
client.force_login( api_request_permissions['user']['add'] )
the_model = model_instance( kwargs_create = self.kwargs_create_item )
url = the_model.get_url( many = True )
the_model.delete()
kwargs = kwargs_api_create.copy()
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
response = client.post(
path = url,
data = kwargs,
content_type = 'application/json'
)
assert response.status_code == 201, response.content
def test_returned_results_only_user_orgs(self, model_instance, model_kwargs, api_request_permissions):
"""Returned results check
Ensure that a query to the viewset endpoint does not return
items that are not part of the users organizations.
"""
if model_kwargs.get('organization', None) is None:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
client = Client()
viewable_organizations = [
api_request_permissions['tenancy']['user'].id,
]
if getattr(self, 'global_organization', None):
# Cater for above test that also has global org
viewable_organizations += [ api_request_permissions['tenancy']['global'] ]
client.force_login( api_request_permissions['user']['view'] )
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['different']
})
model_instance(
kwargs_create = kwargs
)
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['global']
})
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
model_instance(
kwargs_create = kwargs
)
kwargs = self.kwargs_create_item.copy()
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
the_model = model_instance( kwargs_create = kwargs )
response = client.get(
path = the_model.get_url( many = True )
)
# if response.status_code == 405:
# pytest.xfail( reason = 'ViewSet does not have this request method.' )
# elif IsAuthenticatedOrReadOnly in response.renderer_context['view'].permission_classes:
# pytest.xfail( reason = 'ViewSet is public viewable, test is N/A' )
assert response.status_code == 200
contains_different_org: bool = False
for item in response.data['results']:
if 'organization' not in item:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
if(
int(item['organization']['id']) not in viewable_organizations
and
int(item['organization']['id']) != api_request_permissions['tenancy']['global'].id
):
contains_different_org = True
print(f'Failed returned row was: {item}')
assert not contains_different_org
def test_returned_data_from_user_and_global_organizations_only(
self, model_instance, model_kwargs, api_request_permissions
):
"""Check items returned
Items returned from the query Must be from the users organization and
global ONLY!
"""
if model_kwargs.get('organization', None) is None:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
client = Client()
only_from_user_org: bool = True
viewable_organizations = [
api_request_permissions['tenancy']['user'].id,
api_request_permissions['tenancy']['global'].id
]
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['different']
})
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
the_model = model_instance(
kwargs_create = kwargs
)
kwargs = self.kwargs_create_item.copy()
kwargs.update({
'organization': api_request_permissions['tenancy']['global']
})
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
model_instance(
kwargs_create = kwargs
)
client.force_login( api_request_permissions['user']['view'] )
kwargs = self.kwargs_create_item.copy()
kwargs['dob'] = str(random.randint(1972, 2037)) + '-' + str(
random.randint(1, 12)) + '-' + str(random.randint(1, 28))
the_model = model_instance( kwargs_create = kwargs )
response = client.get(
path = the_model.get_url( many = True )
)
assert len(response.data['results']) >= 2 # fail if only one item extist.
for row in response.data['results']:
if 'organization' not in row:
pytest.xfail( reason = 'Model lacks organization field. test is n/a' )
if row['organization']['id'] not in viewable_organizations:
only_from_user_org = False
print(f"Users org: {api_request_permissions['tenancy']['user'].id}")
print(f"global org: {api_request_permissions['tenancy']['global'].id}")
print(f'Failed returned row was: {row}')
assert only_from_user_org

View File

@ -0,0 +1,44 @@
import pytest
from django.test import Client
class AdditionalTestCases:
def test_permission_add(self, model_instance, api_request_permissions,
model_kwargs, kwargs_api_create
):
""" Check correct permission for add
Attempt to add as user with permission
"""
client = Client()
client.force_login( api_request_permissions['user']['add'] )
the_model = model_instance( kwargs_create = model_kwargs )
url = the_model.get_url( many = True )
response = client.post(
path = url,
data = kwargs_api_create,
content_type = 'application/json'
)
assert response.status_code == 201, response.content
def test_returned_data_from_user_and_global_organizations_only(
self
):
"""Check items returned
Items returned from the query Must be from the users organization and
global ONLY!
"""
pytest.mark.xfail( reason = 'model is not for global use' )

View File

@ -0,0 +1,35 @@
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
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_company):
request.cls.kwargs_create_item = kwargs_company.copy()
yield kwargs_company.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -0,0 +1,36 @@
import pytest
from access.tests.functional.entity.test_functional_entity_api_fields import (
EntityAPIInheritedCases
)
@pytest.mark.model_company
class CompanyAPITestCases(
EntityAPIInheritedCases,
):
@property
def parameterized_api_fields(self):
return {
'name': {
'expected': str
}
}
class CompanyAPIInheritedCases(
CompanyAPITestCases,
):
pass
@pytest.mark.module_access
class CompanyAPIPyTest(
CompanyAPITestCases,
):
pass

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,34 @@
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
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_contact):
request.cls.kwargs_create_item = kwargs_contact.copy()
yield kwargs_contact.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -0,0 +1,41 @@
import pytest
from access.tests.functional.person.test_functional_person_api_fields import (
PersonAPIInheritedCases
)
@pytest.mark.model_contact
class ContactAPITestCases(
PersonAPIInheritedCases,
):
property
def parameterized_api_fields(self):
return {
'email': {
'expected': str
},
'directory': {
'expected': bool
}
}
class ContactAPIInheritedCases(
ContactAPITestCases,
):
pass
@pytest.mark.module_access
class ContactAPIPyTest(
ContactAPITestCases,
):
pass

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,34 @@
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
@pytest.fixture( scope = 'class')
def model_kwargs(request, kwargs_entity):
request.cls.kwargs_create_item = kwargs_entity.copy()
yield kwargs_entity.copy()
if hasattr(request.cls, 'kwargs_create_item'):
del request.cls.kwargs_create_item

View File

@ -0,0 +1,48 @@
import pytest
from access.models.entity import Entity
from api.tests.functional.test_functional_api_fields import (
APIFieldsInheritedCases,
)
@pytest.mark.model_entity
class EntityAPITestCases(
APIFieldsInheritedCases,
):
base_model = Entity
@property
def parameterized_api_fields(self):
return {
'entity_type': {
'expected': str
},
'_urls.history': {
'expected': str
},
'_urls.knowledge_base': {
'expected': str
}
}
class EntityAPIInheritedCases(
EntityAPITestCases,
):
pass
@pytest.mark.module_access
class EntityAPIPyTest(
EntityAPITestCases,
):
pass

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