wbcrm 1.56.8__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.
- wbcrm/__init__.py +1 -0
- wbcrm/admin/__init__.py +5 -0
- wbcrm/admin/accounts.py +60 -0
- wbcrm/admin/activities.py +104 -0
- wbcrm/admin/events.py +43 -0
- wbcrm/admin/groups.py +8 -0
- wbcrm/admin/products.py +9 -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 +57 -0
- wbcrm/factories/activities.py +124 -0
- wbcrm/factories/groups.py +24 -0
- wbcrm/factories/products.py +11 -0
- wbcrm/filters/__init__.py +10 -0
- wbcrm/filters/accounts.py +80 -0
- wbcrm/filters/activities.py +204 -0
- wbcrm/filters/groups.py +21 -0
- wbcrm/filters/products.py +38 -0
- wbcrm/filters/signals.py +95 -0
- wbcrm/fixtures/wbcrm.json +1215 -0
- wbcrm/kpi_handlers/activities.py +171 -0
- wbcrm/locale/de/LC_MESSAGES/django.mo +0 -0
- wbcrm/locale/de/LC_MESSAGES/django.po +1557 -0
- wbcrm/locale/de/LC_MESSAGES/django.po.translated +1630 -0
- wbcrm/locale/en/LC_MESSAGES/django.mo +0 -0
- wbcrm/locale/en/LC_MESSAGES/django.po +1466 -0
- wbcrm/locale/fr/LC_MESSAGES/django.mo +0 -0
- wbcrm/locale/fr/LC_MESSAGES/django.po +1467 -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/0017_event.py +40 -0
- wbcrm/migrations/0018_activity_search_vector.py +24 -0
- wbcrm/migrations/__init__.py +0 -0
- wbcrm/models/__init__.py +5 -0
- wbcrm/models/accounts.py +648 -0
- wbcrm/models/activities.py +1419 -0
- wbcrm/models/events.py +15 -0
- wbcrm/models/groups.py +119 -0
- wbcrm/models/llm/activity_summaries.py +41 -0
- wbcrm/models/llm/analyze_relationship.py +50 -0
- wbcrm/models/products.py +86 -0
- wbcrm/models/recurrence.py +280 -0
- wbcrm/preferences.py +13 -0
- wbcrm/report/activity_report.py +110 -0
- wbcrm/serializers/__init__.py +23 -0
- wbcrm/serializers/accounts.py +141 -0
- wbcrm/serializers/activities.py +525 -0
- wbcrm/serializers/groups.py +30 -0
- wbcrm/serializers/products.py +58 -0
- wbcrm/serializers/recurrence.py +91 -0
- wbcrm/serializers/signals.py +71 -0
- wbcrm/static/wbcrm/markdown/documentation/activity.md +86 -0
- wbcrm/static/wbcrm/markdown/documentation/activitytype.md +20 -0
- wbcrm/static/wbcrm/markdown/documentation/group.md +2 -0
- wbcrm/static/wbcrm/markdown/documentation/product.md +11 -0
- wbcrm/synchronization/__init__.py +0 -0
- wbcrm/synchronization/activity/__init__.py +0 -0
- wbcrm/synchronization/activity/admin.py +73 -0
- wbcrm/synchronization/activity/backend.py +214 -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 +406 -0
- wbcrm/synchronization/activity/backends/google/request_utils/__init__.py +16 -0
- wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/create.py +75 -0
- wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/delete.py +78 -0
- wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/update.py +155 -0
- wbcrm/synchronization/activity/backends/google/request_utils/internal_to_external/update.py +181 -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 +217 -0
- wbcrm/synchronization/activity/backends/outlook/__init__.py +0 -0
- wbcrm/synchronization/activity/backends/outlook/backend.py +593 -0
- wbcrm/synchronization/activity/backends/outlook/msgraph.py +436 -0
- wbcrm/synchronization/activity/backends/outlook/parser.py +432 -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 +118 -0
- wbcrm/synchronization/activity/backends/outlook/tests/test_backend.py +274 -0
- wbcrm/synchronization/activity/backends/outlook/tests/test_controller.py +249 -0
- wbcrm/synchronization/activity/backends/outlook/tests/test_parser.py +174 -0
- wbcrm/synchronization/activity/controller.py +627 -0
- wbcrm/synchronization/activity/dynamic_preferences_registry.py +119 -0
- wbcrm/synchronization/activity/preferences.py +27 -0
- wbcrm/synchronization/activity/shortcuts.py +16 -0
- wbcrm/synchronization/activity/tasks.py +21 -0
- wbcrm/synchronization/activity/urls.py +7 -0
- wbcrm/synchronization/activity/utils.py +46 -0
- wbcrm/synchronization/activity/views.py +41 -0
- wbcrm/synchronization/admin.py +1 -0
- wbcrm/synchronization/apps.py +14 -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 +264 -0
- wbcrm/templates/email/activity.html +98 -0
- wbcrm/templates/email/activity_report.html +6 -0
- wbcrm/templates/email/daily_summary.html +72 -0
- wbcrm/templates/email/global_daily_summary.html +85 -0
- wbcrm/tests/__init__.py +0 -0
- wbcrm/tests/accounts/__init__.py +0 -0
- wbcrm/tests/accounts/test_models.py +393 -0
- wbcrm/tests/accounts/test_viewsets.py +88 -0
- wbcrm/tests/conftest.py +76 -0
- wbcrm/tests/disable_signals.py +62 -0
- wbcrm/tests/e2e/__init__.py +1 -0
- wbcrm/tests/e2e/e2e_wbcrm_utility.py +83 -0
- wbcrm/tests/e2e/test_e2e.py +370 -0
- wbcrm/tests/test_assignee_methods.py +40 -0
- wbcrm/tests/test_chartviewsets.py +112 -0
- wbcrm/tests/test_dto.py +64 -0
- wbcrm/tests/test_filters.py +52 -0
- wbcrm/tests/test_models.py +217 -0
- wbcrm/tests/test_recurrence.py +292 -0
- wbcrm/tests/test_report.py +21 -0
- wbcrm/tests/test_serializers.py +171 -0
- wbcrm/tests/test_tasks.py +95 -0
- wbcrm/tests/test_viewsets.py +967 -0
- wbcrm/tests/tests.py +121 -0
- wbcrm/typings.py +109 -0
- wbcrm/urls.py +67 -0
- wbcrm/viewsets/__init__.py +22 -0
- wbcrm/viewsets/accounts.py +122 -0
- wbcrm/viewsets/activities.py +341 -0
- wbcrm/viewsets/buttons/__init__.py +7 -0
- wbcrm/viewsets/buttons/accounts.py +27 -0
- wbcrm/viewsets/buttons/activities.py +89 -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 +444 -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 +25 -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 +38 -0
- wbcrm/viewsets/menu/__init__.py +8 -0
- wbcrm/viewsets/menu/accounts.py +18 -0
- wbcrm/viewsets/menu/activities.py +49 -0
- wbcrm/viewsets/menu/groups.py +16 -0
- wbcrm/viewsets/menu/products.py +20 -0
- wbcrm/viewsets/mixins.py +35 -0
- wbcrm/viewsets/previews/__init__.py +1 -0
- wbcrm/viewsets/previews/activities.py +10 -0
- wbcrm/viewsets/products.py +57 -0
- wbcrm/viewsets/recurrence.py +27 -0
- wbcrm/viewsets/titles/__init__.py +13 -0
- wbcrm/viewsets/titles/accounts.py +23 -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-1.56.8.dist-info/METADATA +11 -0
- wbcrm-1.56.8.dist-info/RECORD +182 -0
- wbcrm-1.56.8.dist-info/WHEEL +5 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
|
|
4
|
+
import xlsxwriter
|
|
5
|
+
from celery import shared_task
|
|
6
|
+
from django.conf import settings
|
|
7
|
+
from django.core.mail import EmailMultiAlternatives
|
|
8
|
+
from django.db.models import Q
|
|
9
|
+
from django.template.loader import get_template
|
|
10
|
+
from django.utils import timezone
|
|
11
|
+
from django.utils.html import strip_tags
|
|
12
|
+
from django.utils.translation import gettext as _
|
|
13
|
+
from wbcore.contrib.directory.models import Person
|
|
14
|
+
from wbcrm.models import Activity, ActivityType
|
|
15
|
+
from xlsxwriter.utility import xl_rowcol_to_cell
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def set_cell(worksheet, row, col, value, width_cols, base_format):
|
|
19
|
+
cell = xl_rowcol_to_cell(row, col)
|
|
20
|
+
if value:
|
|
21
|
+
worksheet.write_string(cell, value, base_format)
|
|
22
|
+
if len(value) > width_cols[col]:
|
|
23
|
+
width_cols[col] = len(value)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@shared_task
|
|
27
|
+
def create_report_and_send(profile_id, employee_id, start_date=None, end_date=None):
|
|
28
|
+
employee = Person.all_objects.get(id=employee_id)
|
|
29
|
+
profile = Person.all_objects.get(id=profile_id)
|
|
30
|
+
if end_date is None:
|
|
31
|
+
end_date = timezone.now()
|
|
32
|
+
if start_date is None:
|
|
33
|
+
start_date = datetime(year=end_date.year, month=1, day=1, hour=0, minute=0, second=0)
|
|
34
|
+
|
|
35
|
+
report = create_report(employee_id, start_date, end_date)
|
|
36
|
+
|
|
37
|
+
html = get_template("email/activity_report.html")
|
|
38
|
+
|
|
39
|
+
context = {"profile": profile, "employee": employee}
|
|
40
|
+
html_content = html.render(context)
|
|
41
|
+
|
|
42
|
+
msg = EmailMultiAlternatives(
|
|
43
|
+
_("Report"), strip_tags(html_content), settings.DEFAULT_FROM_EMAIL, [profile.user_account.email]
|
|
44
|
+
)
|
|
45
|
+
msg.attach_alternative(html_content, "text/html")
|
|
46
|
+
title = _("all")
|
|
47
|
+
if start_date and end_date:
|
|
48
|
+
title = "{}_{}".format(start_date.strftime("%m/%d/%Y-%H:%M:%S"), end_date.strftime("%m/%d/%Y-%H:%M:%S"))
|
|
49
|
+
msg.attach(
|
|
50
|
+
"report_{}_{}.xlsx".format(employee.computed_str, title),
|
|
51
|
+
report.read(),
|
|
52
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
53
|
+
)
|
|
54
|
+
msg.send()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def create_report(employee, start_date, end_date):
|
|
58
|
+
output = BytesIO()
|
|
59
|
+
workbook = xlsxwriter.Workbook(output, {"in_memory": True})
|
|
60
|
+
|
|
61
|
+
base_format = workbook.add_format({"font_name": "Liberation Sans", "font_size": 10})
|
|
62
|
+
bold_format = workbook.add_format({"font_name": "Liberation Sans", "font_size": 10, "bold": True})
|
|
63
|
+
related_activities = Activity.objects.filter(
|
|
64
|
+
Q(participants__id=employee) | Q(creator__id=employee) | Q(assigned_to__id=employee)
|
|
65
|
+
)
|
|
66
|
+
activities = Activity.get_inrange_activities(related_activities, start_date, end_date)
|
|
67
|
+
# HERE STARTS THE FIRST WORKSHEET
|
|
68
|
+
for type in ActivityType.objects.all():
|
|
69
|
+
width_cols = [5, 5, 16, 8, 7, 11, 12]
|
|
70
|
+
activities_type = activities.filter(type=type.id)
|
|
71
|
+
worksheet = workbook.add_worksheet(type.title)
|
|
72
|
+
worksheet.write_string(0, 0, _("Title"), bold_format)
|
|
73
|
+
worksheet.write_string(0, 1, _("Start"), bold_format)
|
|
74
|
+
worksheet.write_string(0, 2, _("Duration (hours)"), bold_format)
|
|
75
|
+
worksheet.write_string(0, 3, _("Location"), bold_format)
|
|
76
|
+
worksheet.write_string(0, 4, _("Creator"), bold_format)
|
|
77
|
+
worksheet.write_string(0, 5, _("Assigned to"), bold_format)
|
|
78
|
+
worksheet.write_string(0, 6, _("Participants"), bold_format)
|
|
79
|
+
|
|
80
|
+
worksheet.write_string(2, 0, _("Total"), bold_format)
|
|
81
|
+
worksheet.write_string(2, 1, str(len(activities_type.all())), bold_format)
|
|
82
|
+
|
|
83
|
+
for row, activity in enumerate(activities_type.all(), start=4):
|
|
84
|
+
hours = (activity.period.upper - activity.period.lower).total_seconds() / 3600
|
|
85
|
+
duration = format(hours, ".2f")
|
|
86
|
+
creator = activity.creator
|
|
87
|
+
creator_name = ""
|
|
88
|
+
if creator:
|
|
89
|
+
creator_name = creator.computed_str
|
|
90
|
+
assigned_to = activity.assigned_to
|
|
91
|
+
assigned_to_name = ""
|
|
92
|
+
if assigned_to:
|
|
93
|
+
assigned_to_name = assigned_to.computed_str
|
|
94
|
+
participants = ", ".join([participant.computed_str for participant in activity.participants.all()])
|
|
95
|
+
|
|
96
|
+
set_cell(worksheet, row, 0, activity.title, width_cols, base_format)
|
|
97
|
+
set_cell(worksheet, row, 1, f"{activity.period.lower:%d.%m.%Y}", width_cols, base_format)
|
|
98
|
+
set_cell(worksheet, row, 2, duration, width_cols, base_format)
|
|
99
|
+
set_cell(worksheet, row, 2, activity.location, width_cols, base_format)
|
|
100
|
+
set_cell(worksheet, row, 4, creator_name, width_cols, base_format)
|
|
101
|
+
set_cell(worksheet, row, 5, assigned_to_name, width_cols, base_format)
|
|
102
|
+
set_cell(worksheet, row, 6, participants, width_cols, base_format)
|
|
103
|
+
|
|
104
|
+
for col, max_width in enumerate(width_cols):
|
|
105
|
+
if max_width > 300:
|
|
106
|
+
max_width = 300
|
|
107
|
+
worksheet.set_column(col, col, max_width)
|
|
108
|
+
workbook.close()
|
|
109
|
+
output.seek(0)
|
|
110
|
+
return output
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .accounts import (
|
|
2
|
+
AccountModelSerializer,
|
|
3
|
+
AccountRepresentationSerializer,
|
|
4
|
+
TerminalAccountRepresentationSerializer,
|
|
5
|
+
AccountRoleModelSerializer,
|
|
6
|
+
AccountRoleTypeRepresentationSerializer,
|
|
7
|
+
)
|
|
8
|
+
from .activities import (
|
|
9
|
+
ActivityModelListSerializer,
|
|
10
|
+
ActivityModelSerializer,
|
|
11
|
+
ReadOnlyActivityModelSerializer,
|
|
12
|
+
ActivityParticipantModelSerializer,
|
|
13
|
+
ActivityRepresentationSerializer,
|
|
14
|
+
ActivityTypeModelSerializer,
|
|
15
|
+
ActivityTypeRepresentationSerializer,
|
|
16
|
+
)
|
|
17
|
+
from .groups import GroupModelSerializer, GroupRepresentationSerializer
|
|
18
|
+
from .products import (
|
|
19
|
+
ProductCompanyRelationshipModelSerializer,
|
|
20
|
+
ProductModelSerializer,
|
|
21
|
+
ProductRepresentationSerializer,
|
|
22
|
+
)
|
|
23
|
+
from .signals import *
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
from django.apps import apps
|
|
2
|
+
from django.contrib.messages import warning
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
4
|
+
from rest_framework.reverse import reverse
|
|
5
|
+
from rest_framework.validators import UniqueValidator
|
|
6
|
+
from wbcore import serializers as wb_serializers
|
|
7
|
+
from wbcore.contrib.authentication.models import User
|
|
8
|
+
from wbcore.contrib.authentication.serializers import UserRepresentationSerializer
|
|
9
|
+
from wbcore.contrib.directory.serializers import EntryRepresentationSerializer
|
|
10
|
+
|
|
11
|
+
from wbcrm.models.accounts import Account, AccountRole, AccountRoleType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AccountRoleTypeRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
15
|
+
class Meta:
|
|
16
|
+
model = AccountRoleType
|
|
17
|
+
fields = ("id", "title")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AccountRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
21
|
+
_detail = wb_serializers.HyperlinkField(reverse_name="wbcrm:account-detail")
|
|
22
|
+
|
|
23
|
+
def get_filter_params(self, request):
|
|
24
|
+
return {"is_active": True}
|
|
25
|
+
|
|
26
|
+
class Meta:
|
|
27
|
+
model = Account
|
|
28
|
+
fields = ("id", "computed_str", "_detail")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TerminalAccountRepresentationSerializer(AccountRepresentationSerializer):
|
|
32
|
+
def get_filter_params(self, request):
|
|
33
|
+
filter_params = {"is_terminal_account": True, "status": "OPEN", "is_active": True}
|
|
34
|
+
if view := request.parser_context.get("view", None):
|
|
35
|
+
if entry_id := view.kwargs.get("entry_id", None):
|
|
36
|
+
filter_params["customer"] = entry_id
|
|
37
|
+
if account_id := view.kwargs.get("account_id", None):
|
|
38
|
+
filter_params["account"] = account_id
|
|
39
|
+
return filter_params
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AccountModelSerializer(wb_serializers.ModelSerializer):
|
|
43
|
+
_parent = AccountRepresentationSerializer(source="parent")
|
|
44
|
+
_owner = EntryRepresentationSerializer(source="owner")
|
|
45
|
+
_group_key = wb_serializers.CharField(read_only=True)
|
|
46
|
+
reference_id = wb_serializers.IntegerField(
|
|
47
|
+
label="Reference ID",
|
|
48
|
+
default=lambda: Account.get_next_available_reference_id(),
|
|
49
|
+
read_only=lambda view: not view.new_mode,
|
|
50
|
+
validators=[UniqueValidator(queryset=Account.objects.all())],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@wb_serializers.register_resource()
|
|
54
|
+
def additional_resources(self, instance, request, user):
|
|
55
|
+
request = self.context["request"]
|
|
56
|
+
|
|
57
|
+
custom_buttons = {
|
|
58
|
+
"childaccounts": reverse("wbcrm:account-childaccount-list", args=[instance.id], request=request),
|
|
59
|
+
"accountroles": reverse("wbcrm:account-accountrole-list", args=[instance.id], request=request),
|
|
60
|
+
"inheritedaccountroles": reverse("wbcrm:account-inheritedrole-list", args=[instance.id], request=request),
|
|
61
|
+
}
|
|
62
|
+
if apps.is_installed("wbportfolio"): # TODO move as signal
|
|
63
|
+
custom_buttons["claims"] = reverse("wbportfolio:account-claim-list", args=[instance.id], request=request)
|
|
64
|
+
|
|
65
|
+
return custom_buttons
|
|
66
|
+
|
|
67
|
+
relationship_status = wb_serializers.RangeSelectField(
|
|
68
|
+
color="rgb(220,20,60)",
|
|
69
|
+
label="Relationship Status",
|
|
70
|
+
start=1,
|
|
71
|
+
end=5,
|
|
72
|
+
step_size=1,
|
|
73
|
+
read_only=True,
|
|
74
|
+
required=False,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
class Meta:
|
|
78
|
+
model = Account
|
|
79
|
+
fields = (
|
|
80
|
+
"id",
|
|
81
|
+
"_group_key",
|
|
82
|
+
"title",
|
|
83
|
+
"status",
|
|
84
|
+
"is_active",
|
|
85
|
+
"is_terminal_account",
|
|
86
|
+
"is_public",
|
|
87
|
+
"_parent",
|
|
88
|
+
"parent",
|
|
89
|
+
"_owner",
|
|
90
|
+
"owner",
|
|
91
|
+
"reference_id",
|
|
92
|
+
"computed_str",
|
|
93
|
+
"relationship_status",
|
|
94
|
+
"relationship_summary",
|
|
95
|
+
"action_plan",
|
|
96
|
+
"_additional_resources",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class AccountRoleModelSerializer(wb_serializers.ModelSerializer):
|
|
101
|
+
_entry = EntryRepresentationSerializer(source="entry")
|
|
102
|
+
_account = AccountRepresentationSerializer(source="account")
|
|
103
|
+
_role_type = AccountRoleTypeRepresentationSerializer(source="role_type")
|
|
104
|
+
is_currently_valid = wb_serializers.BooleanField(read_only=True, default=False)
|
|
105
|
+
|
|
106
|
+
_authorized_hidden_users = UserRepresentationSerializer(source="authorized_hidden_users", many=True)
|
|
107
|
+
|
|
108
|
+
def create(self, validated_data):
|
|
109
|
+
# We return a get or create role because we don't want to leak information on already existing role that the user might not be able to see
|
|
110
|
+
# this way, the account role (even if already existing) will be returned and the permission mixin will take over
|
|
111
|
+
authorized_hidden_users = validated_data.pop("authorized_hidden_users", [])
|
|
112
|
+
instance, created = AccountRole.objects.get_or_create(
|
|
113
|
+
entry=validated_data.pop("entry"), account=validated_data.pop("account"), defaults=validated_data
|
|
114
|
+
)
|
|
115
|
+
if authorized_hidden_users:
|
|
116
|
+
instance.authorized_hidden_users.set(authorized_hidden_users)
|
|
117
|
+
if request := self.context.get("request"):
|
|
118
|
+
if not User.objects.filter(profile_id=instance.entry.id).exists():
|
|
119
|
+
warning(
|
|
120
|
+
request,
|
|
121
|
+
_(
|
|
122
|
+
"The selected entry does not have an associated user account. Note: Notifications cannot be sent to users without an account."
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
return instance
|
|
126
|
+
|
|
127
|
+
class Meta:
|
|
128
|
+
model = AccountRole
|
|
129
|
+
fields = (
|
|
130
|
+
"id",
|
|
131
|
+
"role_type",
|
|
132
|
+
"_role_type",
|
|
133
|
+
"entry",
|
|
134
|
+
"_entry",
|
|
135
|
+
"account",
|
|
136
|
+
"_account",
|
|
137
|
+
"is_currently_valid",
|
|
138
|
+
"is_hidden",
|
|
139
|
+
"authorized_hidden_users",
|
|
140
|
+
"_authorized_hidden_users",
|
|
141
|
+
)
|