wbcrm 2.2.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of wbcrm might be problematic. Click here for more details.

Files changed (155) hide show
  1. wbcrm/__init__.py +1 -0
  2. wbcrm/admin/__init__.py +4 -0
  3. wbcrm/admin/accounts.py +59 -0
  4. wbcrm/admin/activities.py +101 -0
  5. wbcrm/admin/groups.py +7 -0
  6. wbcrm/admin/products.py +8 -0
  7. wbcrm/apps.py +5 -0
  8. wbcrm/configurations/__init__.py +1 -0
  9. wbcrm/configurations/base.py +16 -0
  10. wbcrm/dynamic_preferences_registry.py +38 -0
  11. wbcrm/factories/__init__.py +14 -0
  12. wbcrm/factories/accounts.py +56 -0
  13. wbcrm/factories/activities.py +125 -0
  14. wbcrm/factories/groups.py +23 -0
  15. wbcrm/factories/products.py +10 -0
  16. wbcrm/filters/__init__.py +10 -0
  17. wbcrm/filters/accounts.py +67 -0
  18. wbcrm/filters/activities.py +181 -0
  19. wbcrm/filters/groups.py +20 -0
  20. wbcrm/filters/products.py +37 -0
  21. wbcrm/filters/signals.py +94 -0
  22. wbcrm/migrations/0001_initial_squashed_squashed_0032_productcompanyrelationship_alter_product_prospects_and_more.py +3948 -0
  23. wbcrm/migrations/0002_alter_activity_repeat_choice.py +32 -0
  24. wbcrm/migrations/0003_remove_activity_external_id_and_more.py +63 -0
  25. wbcrm/migrations/0004_alter_activity_status.py +28 -0
  26. wbcrm/migrations/0005_account_accountrole_accountroletype_and_more.py +182 -0
  27. wbcrm/migrations/0006_alter_activity_location.py +17 -0
  28. wbcrm/migrations/0007_alter_account_status.py +23 -0
  29. wbcrm/migrations/0008_alter_activity_options.py +16 -0
  30. wbcrm/migrations/0009_alter_account_is_public.py +19 -0
  31. wbcrm/migrations/0010_alter_account_reference_id.py +17 -0
  32. wbcrm/migrations/0011_activity_summary.py +22 -0
  33. wbcrm/migrations/0012_alter_activity_summary.py +17 -0
  34. wbcrm/migrations/0013_account_action_plan_account_relationship_status_and_more.py +34 -0
  35. wbcrm/migrations/0014_alter_account_relationship_status.py +24 -0
  36. wbcrm/migrations/0015_alter_activity_type.py +23 -0
  37. wbcrm/migrations/0016_auto_20241205_1015.py +106 -0
  38. wbcrm/migrations/__init__.py +0 -0
  39. wbcrm/models/__init__.py +4 -0
  40. wbcrm/models/accounts.py +637 -0
  41. wbcrm/models/activities.py +1335 -0
  42. wbcrm/models/groups.py +118 -0
  43. wbcrm/models/products.py +83 -0
  44. wbcrm/models/recurrence.py +279 -0
  45. wbcrm/preferences.py +14 -0
  46. wbcrm/serializers/__init__.py +23 -0
  47. wbcrm/serializers/accounts.py +126 -0
  48. wbcrm/serializers/activities.py +526 -0
  49. wbcrm/serializers/groups.py +30 -0
  50. wbcrm/serializers/products.py +57 -0
  51. wbcrm/serializers/recurrence.py +90 -0
  52. wbcrm/serializers/signals.py +70 -0
  53. wbcrm/synchronization/__init__.py +0 -0
  54. wbcrm/synchronization/activity/__init__.py +0 -0
  55. wbcrm/synchronization/activity/admin.py +72 -0
  56. wbcrm/synchronization/activity/backend.py +207 -0
  57. wbcrm/synchronization/activity/backends/__init__.py +0 -0
  58. wbcrm/synchronization/activity/backends/google/__init__.py +2 -0
  59. wbcrm/synchronization/activity/backends/google/google_calendar_backend.py +399 -0
  60. wbcrm/synchronization/activity/backends/google/request_utils/__init__.py +16 -0
  61. wbcrm/synchronization/activity/backends/google/tasks.py +21 -0
  62. wbcrm/synchronization/activity/backends/google/tests/__init__.py +0 -0
  63. wbcrm/synchronization/activity/backends/google/tests/conftest.py +1 -0
  64. wbcrm/synchronization/activity/backends/google/tests/test_data.py +81 -0
  65. wbcrm/synchronization/activity/backends/google/tests/test_google_backend.py +319 -0
  66. wbcrm/synchronization/activity/backends/google/tests/test_utils.py +274 -0
  67. wbcrm/synchronization/activity/backends/google/typing_informations.py +139 -0
  68. wbcrm/synchronization/activity/backends/google/utils.py +216 -0
  69. wbcrm/synchronization/activity/backends/outlook/__init__.py +0 -0
  70. wbcrm/synchronization/activity/backends/outlook/backend.py +576 -0
  71. wbcrm/synchronization/activity/backends/outlook/msgraph.py +438 -0
  72. wbcrm/synchronization/activity/backends/outlook/parser.py +423 -0
  73. wbcrm/synchronization/activity/backends/outlook/tests/__init__.py +0 -0
  74. wbcrm/synchronization/activity/backends/outlook/tests/conftest.py +1 -0
  75. wbcrm/synchronization/activity/backends/outlook/tests/fixtures.py +606 -0
  76. wbcrm/synchronization/activity/backends/outlook/tests/test_admin.py +117 -0
  77. wbcrm/synchronization/activity/backends/outlook/tests/test_backend.py +269 -0
  78. wbcrm/synchronization/activity/backends/outlook/tests/test_controller.py +237 -0
  79. wbcrm/synchronization/activity/backends/outlook/tests/test_parser.py +173 -0
  80. wbcrm/synchronization/activity/controller.py +545 -0
  81. wbcrm/synchronization/activity/dynamic_preferences_registry.py +107 -0
  82. wbcrm/synchronization/activity/preferences.py +21 -0
  83. wbcrm/synchronization/activity/shortcuts.py +9 -0
  84. wbcrm/synchronization/activity/signals.py +28 -0
  85. wbcrm/synchronization/activity/tasks.py +21 -0
  86. wbcrm/synchronization/activity/urls.py +6 -0
  87. wbcrm/synchronization/activity/utils.py +46 -0
  88. wbcrm/synchronization/activity/views.py +37 -0
  89. wbcrm/synchronization/admin.py +1 -0
  90. wbcrm/synchronization/apps.py +15 -0
  91. wbcrm/synchronization/dynamic_preferences_registry.py +1 -0
  92. wbcrm/synchronization/management.py +36 -0
  93. wbcrm/synchronization/tasks.py +1 -0
  94. wbcrm/synchronization/urls.py +5 -0
  95. wbcrm/tasks.py +312 -0
  96. wbcrm/tests/__init__.py +0 -0
  97. wbcrm/tests/accounts/__init__.py +0 -0
  98. wbcrm/tests/accounts/test_models.py +380 -0
  99. wbcrm/tests/accounts/test_viewsets.py +87 -0
  100. wbcrm/tests/conftest.py +76 -0
  101. wbcrm/tests/disable_signals.py +52 -0
  102. wbcrm/tests/e2e/__init__.py +1 -0
  103. wbcrm/tests/e2e/e2e_wbcrm_utility.py +82 -0
  104. wbcrm/tests/e2e/test_e2e.py +369 -0
  105. wbcrm/tests/test_assignee_methods.py +39 -0
  106. wbcrm/tests/test_chartviewsets.py +111 -0
  107. wbcrm/tests/test_dto.py +63 -0
  108. wbcrm/tests/test_filters.py +51 -0
  109. wbcrm/tests/test_models.py +216 -0
  110. wbcrm/tests/test_recurrence.py +291 -0
  111. wbcrm/tests/test_report.py +20 -0
  112. wbcrm/tests/test_serializers.py +170 -0
  113. wbcrm/tests/test_tasks.py +94 -0
  114. wbcrm/tests/test_viewsets.py +967 -0
  115. wbcrm/tests/tests.py +120 -0
  116. wbcrm/typings.py +107 -0
  117. wbcrm/urls.py +67 -0
  118. wbcrm/viewsets/__init__.py +22 -0
  119. wbcrm/viewsets/accounts.py +121 -0
  120. wbcrm/viewsets/activities.py +315 -0
  121. wbcrm/viewsets/buttons/__init__.py +7 -0
  122. wbcrm/viewsets/buttons/accounts.py +27 -0
  123. wbcrm/viewsets/buttons/activities.py +68 -0
  124. wbcrm/viewsets/buttons/signals.py +17 -0
  125. wbcrm/viewsets/display/__init__.py +12 -0
  126. wbcrm/viewsets/display/accounts.py +110 -0
  127. wbcrm/viewsets/display/activities.py +443 -0
  128. wbcrm/viewsets/display/groups.py +22 -0
  129. wbcrm/viewsets/display/products.py +105 -0
  130. wbcrm/viewsets/endpoints/__init__.py +8 -0
  131. wbcrm/viewsets/endpoints/accounts.py +32 -0
  132. wbcrm/viewsets/endpoints/activities.py +30 -0
  133. wbcrm/viewsets/endpoints/groups.py +7 -0
  134. wbcrm/viewsets/endpoints/products.py +9 -0
  135. wbcrm/viewsets/groups.py +37 -0
  136. wbcrm/viewsets/menu/__init__.py +8 -0
  137. wbcrm/viewsets/menu/accounts.py +18 -0
  138. wbcrm/viewsets/menu/activities.py +61 -0
  139. wbcrm/viewsets/menu/groups.py +16 -0
  140. wbcrm/viewsets/menu/products.py +20 -0
  141. wbcrm/viewsets/mixins.py +34 -0
  142. wbcrm/viewsets/previews/__init__.py +1 -0
  143. wbcrm/viewsets/previews/activities.py +10 -0
  144. wbcrm/viewsets/products.py +56 -0
  145. wbcrm/viewsets/recurrence.py +26 -0
  146. wbcrm/viewsets/titles/__init__.py +13 -0
  147. wbcrm/viewsets/titles/accounts.py +22 -0
  148. wbcrm/viewsets/titles/activities.py +61 -0
  149. wbcrm/viewsets/titles/products.py +13 -0
  150. wbcrm/viewsets/titles/utils.py +46 -0
  151. wbcrm/workflows/__init__.py +1 -0
  152. wbcrm/workflows/assignee_methods.py +25 -0
  153. wbcrm-2.2.1.dist-info/METADATA +11 -0
  154. wbcrm-2.2.1.dist-info/RECORD +155 -0
  155. wbcrm-2.2.1.dist-info/WHEEL +5 -0
wbcrm/tests/tests.py ADDED
@@ -0,0 +1,120 @@
1
+ import pytest
2
+ from django.dispatch import receiver
3
+ from rest_framework import status
4
+ from rest_framework.test import APIRequestFactory
5
+ from termcolor import colored
6
+ from wbcore.contrib.directory.factories import PersonFactory
7
+ from wbcore.test import GenerateTest, default_config
8
+ from wbcore.test.mixins import TestViewSet
9
+ from wbcore.test.signals import custom_update_kwargs
10
+ from wbcore.test.utils import get_data_from_factory, get_kwargs, get_or_create_superuser
11
+ from wbcrm.factories import AccountFactory
12
+ from wbcrm.viewsets import ActivityParticipantModelViewSet
13
+ from wbcrm.viewsets.accounts import (
14
+ ChildAccountAccountModelViewSet,
15
+ InheritedAccountRoleAccountModelViewSet,
16
+ )
17
+
18
+ config = {}
19
+ for key, value in default_config.items():
20
+ config[key] = list(
21
+ filter(
22
+ lambda x: x.__module__.startswith("wbcrm")
23
+ and x.__name__
24
+ not in [
25
+ "ActivityParticipantModelViewSet",
26
+ "InheritedAccountRoleAccountModelViewSet",
27
+ "AccountRoleAccountModelViewSet",
28
+ "ChildAccountAccountModelViewSet",
29
+ ],
30
+ value,
31
+ )
32
+ )
33
+
34
+
35
+ @pytest.mark.django_db
36
+ @GenerateTest(config)
37
+ class TestProject:
38
+ pass
39
+
40
+
41
+ # ActivityParticipantModelViewSet Test
42
+ class ActivityParticipantTestViewSet(TestViewSet):
43
+ def _get_mixins_update_data(self, type):
44
+ api_request = APIRequestFactory()
45
+ superuser = get_or_create_superuser()
46
+ obj = self.factory()
47
+ data = get_data_from_factory(obj, self.mvs, superuser=superuser, update=True)
48
+ data["participant"] = PersonFactory().id
49
+ if type == "PATCH":
50
+ request = api_request.patch("", data)
51
+ else: # "UPDATE"
52
+ request = api_request.put("", data)
53
+ request.user = superuser
54
+ kwargs = get_kwargs(obj, self.mvs, request=request, data=data)
55
+ return obj, request, kwargs, data
56
+
57
+ def test_patch_request(self):
58
+ obj, request, kwargs, data = self._get_mixins_update_data("PATCH")
59
+ vs = self.mvs.as_view({"patch": "partial_update"})
60
+ ep = self._get_endpoint_config(request, kwargs, obj)
61
+ ep_update = ep.get_instance_endpoint()
62
+ response = vs(request, **kwargs, data=data)
63
+ if ep_update:
64
+ assert response.status_code == status.HTTP_200_OK, str(response.status_code) + f" == 200 ({response.data})"
65
+ assert response.data.get("instance"), str(response.data.get("instance")) + " should not be empty"
66
+ else:
67
+ assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED, (
68
+ str(response.status_code) + f" == 405 ({response.data})"
69
+ )
70
+ print(f"- {self.__class__.__name__}:test_patchviewset", colored("PASSED", "green")) # noqa: T201
71
+
72
+ def test_update_request(self):
73
+ obj, request, kwargs, _ = self._get_mixins_update_data("UPDATE")
74
+ vs = self.mvs.as_view({"put": "update"})
75
+ ep = self._get_endpoint_config(request, kwargs, obj)
76
+ ep_update = ep.get_instance_endpoint()
77
+ response = vs(request, **kwargs)
78
+ if ep_update:
79
+ assert response.status_code == status.HTTP_200_OK, str(response.status_code) + f" == 200 ({response.data})"
80
+ assert response.data.get("instance"), str(response.data.get("instance")) + " should not be empty"
81
+ else:
82
+ assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED, (
83
+ str(response.status_code) + f" == 405 ({response.data})"
84
+ )
85
+ print(f"- {self.__class__.__name__}:test_update_request", colored("PASSED", "green")) # noqa: T201
86
+
87
+
88
+ class GenerateActivityParticipantTest(GenerateTest):
89
+ def test_modelviewsets(_self, mvs, client):
90
+ my_test = ActivityParticipantTestViewSet(mvs)
91
+ my_test.execute_test_list_endpoint(client)
92
+ my_test.execute_test_detail_endpoint()
93
+
94
+
95
+ @pytest.mark.django_db
96
+ @GenerateActivityParticipantTest({"viewsets": [ActivityParticipantModelViewSet]})
97
+ class TestActivityParticipant:
98
+ pass
99
+
100
+
101
+ @receiver(custom_update_kwargs, sender=ChildAccountAccountModelViewSet)
102
+ def receive_kwargs_child_account(sender, *args, **kwargs):
103
+ if obj := kwargs.get("obj_factory"):
104
+ parent = AccountFactory.create()
105
+ obj.parent = parent
106
+ obj.save()
107
+ return {"account_id": parent.id}
108
+ return {}
109
+
110
+
111
+ @receiver(custom_update_kwargs, sender=InheritedAccountRoleAccountModelViewSet)
112
+ def receive_kwargs_inherited_account_role(sender, *args, **kwargs):
113
+ if obj := kwargs.get("obj_factory"):
114
+ parent_account = AccountFactory.create()
115
+ child_account = obj.account
116
+ child_account.parent = parent_account
117
+ child_account.save()
118
+ obj.account = parent_account
119
+ obj.save()
120
+ return {"account_id": child_account.id}
wbcrm/typings.py ADDED
@@ -0,0 +1,107 @@
1
+ import enum
2
+ from dataclasses import dataclass, field
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from psycopg.types.range import TimestamptzRange
7
+ from wbcore.contrib.agenda.typings import ConferenceRoom
8
+ from wbcore.contrib.directory.typings import Person
9
+
10
+
11
+ @dataclass
12
+ class User:
13
+ metadata: dict[str, Any]
14
+ id: str = None
15
+
16
+ def __eq__(self, other):
17
+ if other and (self.id and other.id and self.id == other.id):
18
+ return True
19
+ return super().__eq__(other)
20
+
21
+
22
+ @dataclass
23
+ class ParticipantStatus:
24
+ class ParticipationStatus(enum.Enum):
25
+ CANCELLED = "CANCELLED"
26
+ MAYBE = "MAYBE"
27
+ ATTENDS = "ATTENDS"
28
+ NOTRESPONDED = "NOTRESPONDED"
29
+ ATTENDS_DIGITALLY = "ATTENDS_DIGITALLY"
30
+
31
+ person: Person
32
+ status_changed: datetime = None
33
+ status: str = ParticipationStatus.NOTRESPONDED.name
34
+ activity: "Activity" = None
35
+ id: str = None
36
+
37
+ def __eq__(self, other):
38
+ if other and (
39
+ (self.id and other.id and self.id == other.id)
40
+ or (self.person == other.person and self.activity == other.activity)
41
+ ):
42
+ return True
43
+ return super().__eq__(other)
44
+
45
+
46
+ @dataclass
47
+ class Activity:
48
+ class ReoccuranceChoice(enum.Enum):
49
+ NEVER = "NEVER"
50
+ BUSINESS_DAILY = "RRULE:FREQ=DAILY;INTERVAL=1;WKST=MO;BYDAY=MO,TU,WE,TH,FR"
51
+ DAILY = "RRULE:FREQ=DAILY"
52
+ WEEKLY = "RRULE:FREQ=WEEKLY"
53
+ BIWEEKLY = "RRULE:FREQ=WEEKLY;INTERVAL=2"
54
+ MONTHLY = "RRULE:FREQ=MONTHLY"
55
+ QUARTERLY = "RRULE:FREQ=MONTHLY;INTERVAL=3"
56
+ YEARLY = "RRULE:FREQ=YEARLY"
57
+
58
+ class Visibility(enum.Enum):
59
+ PUBLIC = "PUBLIC"
60
+ PRIVATE = "PRIVATE"
61
+ CONFIDENTIAL = "CONFIDENTIAL"
62
+
63
+ class ReminderChoice(enum.Enum):
64
+ NEVER = "NEVER"
65
+ EVENT_TIME = "EVENT_TIME"
66
+ MINUTES_5 = "MINUTES_5"
67
+ MINUTES_15 = "MINUTES_15"
68
+ MINUTES_30 = "MINUTES_30"
69
+ HOURS_1 = "HOURS_1"
70
+ HOURS_2 = "HOURS_2"
71
+ HOURS_12 = "HOURS_12"
72
+ WEEKS_1 = "WEEKS_1"
73
+
74
+ @property
75
+ def is_recurrent(self):
76
+ return self.repeat_choice != self.ReoccuranceChoice.NEVER.name
77
+
78
+ metadata: dict[str, Any]
79
+ title: str
80
+ period: TimestamptzRange = None
81
+ description: str = ""
82
+ participants: list["ParticipantStatus"] = field(default_factory=list)
83
+ creator: Person = None
84
+ visibility: str = Visibility.PUBLIC
85
+ reminder_choice: str = ReminderChoice.MINUTES_15.name
86
+ is_cancelled: bool = False
87
+ all_day: bool = False
88
+ online_meeting: bool = False
89
+ location: str = None
90
+ conference_room: ConferenceRoom = None
91
+ id: str = None
92
+
93
+ # parent_occurrence: "Activity" = None
94
+ recurring_activities: list["Activity"] = field(default_factory=list)
95
+ invalid_recurring_activities: list["Activity"] = field(default_factory=list)
96
+ is_root: bool = False
97
+ is_leaf: bool = False
98
+ exclude_from_propagation: bool = False
99
+ propagate_for_all_children: bool = False
100
+ recurrence_end: datetime = None
101
+ recurrence_count: int = 0
102
+ repeat_choice: str = ReoccuranceChoice.NEVER.name
103
+
104
+ def __eq__(self, other):
105
+ if other and (self.id and other.id and self.id == other.id):
106
+ return True
107
+ return super().__eq__(other)
wbcrm/urls.py ADDED
@@ -0,0 +1,67 @@
1
+ from django.urls import include, path
2
+ from wbcore.routers import WBCoreRouter
3
+
4
+ from . import viewsets
5
+
6
+ router = WBCoreRouter()
7
+
8
+ # Representations
9
+ router.register(r"activityrepresentation", viewsets.ActivityRepresentationViewSet, basename="activityrepresentation")
10
+ router.register(r"grouprepresentation", viewsets.GroupRepresentationViewSet, basename="grouprepresentation")
11
+ router.register(r"group", viewsets.GroupModelViewSet, basename="group")
12
+ router.register(r"activitytype", viewsets.ActivityTypeModelViewSet, basename="activitytype")
13
+ router.register(
14
+ r"activitytyperepresentation", viewsets.ActivityTypeRepresentationViewSet, basename="activitytyperepresentation"
15
+ )
16
+
17
+ router.register(r"product", viewsets.ProductModelViewSet, basename="product")
18
+ router.register(
19
+ r"productrepresentation",
20
+ viewsets.ProductRepresentationViewSet,
21
+ basename="productrepresentation",
22
+ )
23
+
24
+
25
+ # Activity
26
+ router.register(r"activity", viewsets.ActivityViewSet, basename="activity")
27
+ # used to create new activity instances
28
+
29
+ router.register(r"activitychart", viewsets.ActivityChartModelViewSet, basename="activitychart")
30
+
31
+
32
+ activity_router = WBCoreRouter()
33
+ activity_router.register(
34
+ r"activity-participant",
35
+ viewsets.ActivityParticipantModelViewSet,
36
+ basename="activity-participant",
37
+ )
38
+
39
+ company_router = WBCoreRouter()
40
+ company_router.register(
41
+ r"company-interestedproduct",
42
+ viewsets.ProductCompanyRelationshipCompanyModelViewSet,
43
+ basename="company-interestedproduct",
44
+ )
45
+
46
+ router.register(r"account", viewsets.AccountModelViewSet, basename="account")
47
+ router.register(r"accountrepresentation", viewsets.AccountRepresentationViewSet, basename="accountrepresentation")
48
+ router.register(
49
+ r"accountroletyperepresentation",
50
+ viewsets.AccountRoleTypeRepresentationViewSet,
51
+ basename="accountroletyperepresentation",
52
+ )
53
+
54
+ account_router = WBCoreRouter()
55
+ account_router.register(r"childaccount", viewsets.ChildAccountAccountModelViewSet, basename="account-childaccount")
56
+ account_router.register(r"accountrole", viewsets.AccountRoleAccountModelViewSet, basename="account-accountrole")
57
+ account_router.register(
58
+ r"inheritedrole", viewsets.InheritedAccountRoleAccountModelViewSet, basename="account-inheritedrole"
59
+ )
60
+
61
+ urlpatterns = [
62
+ path("", include(router.urls)),
63
+ path("activity/<int:activity_id>/", include(activity_router.urls)),
64
+ path("company/<int:company_id>/", include(company_router.urls)),
65
+ path("sync/", include(("wbcrm.synchronization.urls", "sync"), namespace="sync")),
66
+ path("account/<int:account_id>/", include(account_router.urls)),
67
+ ]
@@ -0,0 +1,22 @@
1
+ from .accounts import (
2
+ AccountModelViewSet,
3
+ AccountRepresentationViewSet,
4
+ AccountRoleAccountModelViewSet,
5
+ AccountRoleTypeRepresentationViewSet,
6
+ ChildAccountAccountModelViewSet,
7
+ InheritedAccountRoleAccountModelViewSet,
8
+ )
9
+ from .activities import (
10
+ ActivityChartModelViewSet,
11
+ ActivityParticipantModelViewSet,
12
+ ActivityRepresentationViewSet,
13
+ ActivityTypeModelViewSet,
14
+ ActivityTypeRepresentationViewSet,
15
+ ActivityViewSet,
16
+ )
17
+ from .groups import GroupModelViewSet, GroupRepresentationViewSet
18
+ from .products import (
19
+ ProductCompanyRelationshipCompanyModelViewSet,
20
+ ProductModelViewSet,
21
+ ProductRepresentationViewSet,
22
+ )
@@ -0,0 +1,121 @@
1
+ from django.db.models import Case, Exists, F, IntegerField, OuterRef, When
2
+ from django.shortcuts import get_object_or_404
3
+ from django.utils.functional import cached_property
4
+ from rest_framework.exceptions import AuthenticationFailed
5
+ from wbcore import viewsets
6
+ from wbcore.utils.views import MergeMixin
7
+ from wbcrm.filters import AccountFilter, AccountRoleFilterSet
8
+ from wbcrm.models.accounts import Account, AccountRole, AccountRoleType
9
+ from wbcrm.serializers.accounts import (
10
+ AccountModelSerializer,
11
+ AccountRepresentationSerializer,
12
+ AccountRoleModelSerializer,
13
+ AccountRoleTypeRepresentationSerializer,
14
+ )
15
+ from wbcrm.viewsets.buttons import AccountButtonConfig
16
+ from wbcrm.viewsets.display import (
17
+ AccountDisplayConfig,
18
+ AccountRoleAccountDisplayConfig,
19
+ InheritedAccountRoleAccountDisplayConfig,
20
+ )
21
+ from wbcrm.viewsets.endpoints import (
22
+ AccountRoleAccountEndpointConfig,
23
+ ChildAccountAccountEndpointConfig,
24
+ InheritedAccountRoleAccountEndpointConfig,
25
+ )
26
+ from wbcrm.viewsets.titles import (
27
+ AccountRoleAccountTitleConfig,
28
+ AccountTitleConfig,
29
+ ChildAccountAccountTitleConfig,
30
+ )
31
+
32
+ from .mixins import AccountPermissionMixin, AccountRolePermissionMixin
33
+
34
+
35
+ class AccountRoleTypeRepresentationViewSet(viewsets.RepresentationViewSet):
36
+ ordering_fields = search_fields = ordering = ("title",)
37
+ serializer_class = AccountRoleTypeRepresentationSerializer
38
+ queryset = AccountRoleType.objects.all()
39
+
40
+
41
+ class AccountRepresentationViewSet(AccountPermissionMixin, viewsets.RepresentationViewSet):
42
+ ordering_fields = ("title",)
43
+ search_fields = ["computed_str", "owner__computed_str"]
44
+ filterset_class = AccountFilter
45
+
46
+ serializer_class = AccountRepresentationSerializer
47
+ queryset = Account.objects.all()
48
+ ordering = ["title"]
49
+
50
+
51
+ class AccountModelViewSet(MergeMixin, AccountPermissionMixin, viewsets.ModelViewSet):
52
+ serializer_class = AccountModelSerializer
53
+ filterset_class = AccountFilter
54
+ queryset = Account.objects.select_related("owner", "parent").annotate(
55
+ has_children=Exists(Account.objects.filter(parent=OuterRef("pk"))),
56
+ _group_key=Case(When(has_children=True, then=F("id")), default=None, output_field=IntegerField()),
57
+ )
58
+
59
+ ordering = ordering_fields = [
60
+ "title",
61
+ "owner__computed_str",
62
+ "reference_id",
63
+ "is_terminal_account",
64
+ "is_public",
65
+ "is_active",
66
+ ]
67
+ search_fields = ["computed_str", "owner__computed_str"]
68
+
69
+ display_config_class = AccountDisplayConfig
70
+ title_config_class = AccountTitleConfig
71
+ button_config_class = AccountButtonConfig
72
+
73
+ def get_merged_object_representation_serializer(self):
74
+ return AccountRepresentationSerializer
75
+
76
+
77
+ class ChildAccountAccountModelViewSet(AccountModelViewSet):
78
+ title_config_class = ChildAccountAccountTitleConfig
79
+ endpoint_config_class = ChildAccountAccountEndpointConfig
80
+
81
+ def dispatch(self, *args, **kwargs):
82
+ kwargs["parent_id"] = kwargs.get("account_id", None)
83
+ return super().dispatch(*args, **kwargs)
84
+
85
+ def get_queryset(self):
86
+ return super().get_queryset().filter(parent_id=self.kwargs["account_id"])
87
+
88
+
89
+ class AccountRoleAccountModelViewSet(AccountRolePermissionMixin, viewsets.ModelViewSet):
90
+ display_config_class = AccountRoleAccountDisplayConfig
91
+ title_config_class = AccountRoleAccountTitleConfig
92
+ endpoint_config_class = AccountRoleAccountEndpointConfig
93
+ serializer_class = AccountRoleModelSerializer
94
+
95
+ filterset_class = AccountRoleFilterSet
96
+ queryset = AccountRole.objects.select_related(
97
+ "entry",
98
+ "account",
99
+ )
100
+ ordering = ["role_type", "id"]
101
+
102
+ @cached_property
103
+ def account(self):
104
+ account = get_object_or_404(Account, pk=self.kwargs["account_id"])
105
+ if not account.can_see_account(self.request.user):
106
+ raise AuthenticationFailed()
107
+ return account
108
+
109
+ def get_queryset(self):
110
+ return super().get_queryset().filter(account=self.account)
111
+
112
+
113
+ class InheritedAccountRoleAccountModelViewSet(AccountRoleAccountModelViewSet):
114
+ display_config_class = InheritedAccountRoleAccountDisplayConfig
115
+ endpoint_config_class = InheritedAccountRoleAccountEndpointConfig
116
+ READ_ONLY = True
117
+
118
+ def get_queryset(self):
119
+ return AccountRole.objects.filter(
120
+ id__in=self.account.get_inherited_roles_for_account().values("id")
121
+ ).filter_for_user(self.request.user, validity_date=self.validity_date)