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.
- wbcrm/__init__.py +1 -0
- wbcrm/admin/__init__.py +4 -0
- wbcrm/admin/accounts.py +59 -0
- wbcrm/admin/activities.py +101 -0
- wbcrm/admin/groups.py +7 -0
- wbcrm/admin/products.py +8 -0
- wbcrm/apps.py +5 -0
- wbcrm/configurations/__init__.py +1 -0
- wbcrm/configurations/base.py +16 -0
- wbcrm/dynamic_preferences_registry.py +38 -0
- wbcrm/factories/__init__.py +14 -0
- wbcrm/factories/accounts.py +56 -0
- wbcrm/factories/activities.py +125 -0
- wbcrm/factories/groups.py +23 -0
- wbcrm/factories/products.py +10 -0
- wbcrm/filters/__init__.py +10 -0
- wbcrm/filters/accounts.py +67 -0
- wbcrm/filters/activities.py +181 -0
- wbcrm/filters/groups.py +20 -0
- wbcrm/filters/products.py +37 -0
- wbcrm/filters/signals.py +94 -0
- wbcrm/migrations/0001_initial_squashed_squashed_0032_productcompanyrelationship_alter_product_prospects_and_more.py +3948 -0
- wbcrm/migrations/0002_alter_activity_repeat_choice.py +32 -0
- wbcrm/migrations/0003_remove_activity_external_id_and_more.py +63 -0
- wbcrm/migrations/0004_alter_activity_status.py +28 -0
- wbcrm/migrations/0005_account_accountrole_accountroletype_and_more.py +182 -0
- wbcrm/migrations/0006_alter_activity_location.py +17 -0
- wbcrm/migrations/0007_alter_account_status.py +23 -0
- wbcrm/migrations/0008_alter_activity_options.py +16 -0
- wbcrm/migrations/0009_alter_account_is_public.py +19 -0
- wbcrm/migrations/0010_alter_account_reference_id.py +17 -0
- wbcrm/migrations/0011_activity_summary.py +22 -0
- wbcrm/migrations/0012_alter_activity_summary.py +17 -0
- wbcrm/migrations/0013_account_action_plan_account_relationship_status_and_more.py +34 -0
- wbcrm/migrations/0014_alter_account_relationship_status.py +24 -0
- wbcrm/migrations/0015_alter_activity_type.py +23 -0
- wbcrm/migrations/0016_auto_20241205_1015.py +106 -0
- wbcrm/migrations/__init__.py +0 -0
- wbcrm/models/__init__.py +4 -0
- wbcrm/models/accounts.py +637 -0
- wbcrm/models/activities.py +1335 -0
- wbcrm/models/groups.py +118 -0
- wbcrm/models/products.py +83 -0
- wbcrm/models/recurrence.py +279 -0
- wbcrm/preferences.py +14 -0
- wbcrm/serializers/__init__.py +23 -0
- wbcrm/serializers/accounts.py +126 -0
- wbcrm/serializers/activities.py +526 -0
- wbcrm/serializers/groups.py +30 -0
- wbcrm/serializers/products.py +57 -0
- wbcrm/serializers/recurrence.py +90 -0
- wbcrm/serializers/signals.py +70 -0
- wbcrm/synchronization/__init__.py +0 -0
- wbcrm/synchronization/activity/__init__.py +0 -0
- wbcrm/synchronization/activity/admin.py +72 -0
- wbcrm/synchronization/activity/backend.py +207 -0
- wbcrm/synchronization/activity/backends/__init__.py +0 -0
- wbcrm/synchronization/activity/backends/google/__init__.py +2 -0
- wbcrm/synchronization/activity/backends/google/google_calendar_backend.py +399 -0
- wbcrm/synchronization/activity/backends/google/request_utils/__init__.py +16 -0
- wbcrm/synchronization/activity/backends/google/tasks.py +21 -0
- wbcrm/synchronization/activity/backends/google/tests/__init__.py +0 -0
- wbcrm/synchronization/activity/backends/google/tests/conftest.py +1 -0
- wbcrm/synchronization/activity/backends/google/tests/test_data.py +81 -0
- wbcrm/synchronization/activity/backends/google/tests/test_google_backend.py +319 -0
- wbcrm/synchronization/activity/backends/google/tests/test_utils.py +274 -0
- wbcrm/synchronization/activity/backends/google/typing_informations.py +139 -0
- wbcrm/synchronization/activity/backends/google/utils.py +216 -0
- wbcrm/synchronization/activity/backends/outlook/__init__.py +0 -0
- wbcrm/synchronization/activity/backends/outlook/backend.py +576 -0
- wbcrm/synchronization/activity/backends/outlook/msgraph.py +438 -0
- wbcrm/synchronization/activity/backends/outlook/parser.py +423 -0
- wbcrm/synchronization/activity/backends/outlook/tests/__init__.py +0 -0
- wbcrm/synchronization/activity/backends/outlook/tests/conftest.py +1 -0
- wbcrm/synchronization/activity/backends/outlook/tests/fixtures.py +606 -0
- wbcrm/synchronization/activity/backends/outlook/tests/test_admin.py +117 -0
- wbcrm/synchronization/activity/backends/outlook/tests/test_backend.py +269 -0
- wbcrm/synchronization/activity/backends/outlook/tests/test_controller.py +237 -0
- wbcrm/synchronization/activity/backends/outlook/tests/test_parser.py +173 -0
- wbcrm/synchronization/activity/controller.py +545 -0
- wbcrm/synchronization/activity/dynamic_preferences_registry.py +107 -0
- wbcrm/synchronization/activity/preferences.py +21 -0
- wbcrm/synchronization/activity/shortcuts.py +9 -0
- wbcrm/synchronization/activity/signals.py +28 -0
- wbcrm/synchronization/activity/tasks.py +21 -0
- wbcrm/synchronization/activity/urls.py +6 -0
- wbcrm/synchronization/activity/utils.py +46 -0
- wbcrm/synchronization/activity/views.py +37 -0
- wbcrm/synchronization/admin.py +1 -0
- wbcrm/synchronization/apps.py +15 -0
- wbcrm/synchronization/dynamic_preferences_registry.py +1 -0
- wbcrm/synchronization/management.py +36 -0
- wbcrm/synchronization/tasks.py +1 -0
- wbcrm/synchronization/urls.py +5 -0
- wbcrm/tasks.py +312 -0
- wbcrm/tests/__init__.py +0 -0
- wbcrm/tests/accounts/__init__.py +0 -0
- wbcrm/tests/accounts/test_models.py +380 -0
- wbcrm/tests/accounts/test_viewsets.py +87 -0
- wbcrm/tests/conftest.py +76 -0
- wbcrm/tests/disable_signals.py +52 -0
- wbcrm/tests/e2e/__init__.py +1 -0
- wbcrm/tests/e2e/e2e_wbcrm_utility.py +82 -0
- wbcrm/tests/e2e/test_e2e.py +369 -0
- wbcrm/tests/test_assignee_methods.py +39 -0
- wbcrm/tests/test_chartviewsets.py +111 -0
- wbcrm/tests/test_dto.py +63 -0
- wbcrm/tests/test_filters.py +51 -0
- wbcrm/tests/test_models.py +216 -0
- wbcrm/tests/test_recurrence.py +291 -0
- wbcrm/tests/test_report.py +20 -0
- wbcrm/tests/test_serializers.py +170 -0
- wbcrm/tests/test_tasks.py +94 -0
- wbcrm/tests/test_viewsets.py +967 -0
- wbcrm/tests/tests.py +120 -0
- wbcrm/typings.py +107 -0
- wbcrm/urls.py +67 -0
- wbcrm/viewsets/__init__.py +22 -0
- wbcrm/viewsets/accounts.py +121 -0
- wbcrm/viewsets/activities.py +315 -0
- wbcrm/viewsets/buttons/__init__.py +7 -0
- wbcrm/viewsets/buttons/accounts.py +27 -0
- wbcrm/viewsets/buttons/activities.py +68 -0
- wbcrm/viewsets/buttons/signals.py +17 -0
- wbcrm/viewsets/display/__init__.py +12 -0
- wbcrm/viewsets/display/accounts.py +110 -0
- wbcrm/viewsets/display/activities.py +443 -0
- wbcrm/viewsets/display/groups.py +22 -0
- wbcrm/viewsets/display/products.py +105 -0
- wbcrm/viewsets/endpoints/__init__.py +8 -0
- wbcrm/viewsets/endpoints/accounts.py +32 -0
- wbcrm/viewsets/endpoints/activities.py +30 -0
- wbcrm/viewsets/endpoints/groups.py +7 -0
- wbcrm/viewsets/endpoints/products.py +9 -0
- wbcrm/viewsets/groups.py +37 -0
- wbcrm/viewsets/menu/__init__.py +8 -0
- wbcrm/viewsets/menu/accounts.py +18 -0
- wbcrm/viewsets/menu/activities.py +61 -0
- wbcrm/viewsets/menu/groups.py +16 -0
- wbcrm/viewsets/menu/products.py +20 -0
- wbcrm/viewsets/mixins.py +34 -0
- wbcrm/viewsets/previews/__init__.py +1 -0
- wbcrm/viewsets/previews/activities.py +10 -0
- wbcrm/viewsets/products.py +56 -0
- wbcrm/viewsets/recurrence.py +26 -0
- wbcrm/viewsets/titles/__init__.py +13 -0
- wbcrm/viewsets/titles/accounts.py +22 -0
- wbcrm/viewsets/titles/activities.py +61 -0
- wbcrm/viewsets/titles/products.py +13 -0
- wbcrm/viewsets/titles/utils.py +46 -0
- wbcrm/workflows/__init__.py +1 -0
- wbcrm/workflows/assignee_methods.py +25 -0
- wbcrm-2.2.1.dist-info/METADATA +11 -0
- wbcrm-2.2.1.dist-info/RECORD +155 -0
- 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)
|