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.
Files changed (182) hide show
  1. wbcrm/__init__.py +1 -0
  2. wbcrm/admin/__init__.py +5 -0
  3. wbcrm/admin/accounts.py +60 -0
  4. wbcrm/admin/activities.py +104 -0
  5. wbcrm/admin/events.py +43 -0
  6. wbcrm/admin/groups.py +8 -0
  7. wbcrm/admin/products.py +9 -0
  8. wbcrm/apps.py +5 -0
  9. wbcrm/configurations/__init__.py +1 -0
  10. wbcrm/configurations/base.py +16 -0
  11. wbcrm/dynamic_preferences_registry.py +38 -0
  12. wbcrm/factories/__init__.py +14 -0
  13. wbcrm/factories/accounts.py +57 -0
  14. wbcrm/factories/activities.py +124 -0
  15. wbcrm/factories/groups.py +24 -0
  16. wbcrm/factories/products.py +11 -0
  17. wbcrm/filters/__init__.py +10 -0
  18. wbcrm/filters/accounts.py +80 -0
  19. wbcrm/filters/activities.py +204 -0
  20. wbcrm/filters/groups.py +21 -0
  21. wbcrm/filters/products.py +38 -0
  22. wbcrm/filters/signals.py +95 -0
  23. wbcrm/fixtures/wbcrm.json +1215 -0
  24. wbcrm/kpi_handlers/activities.py +171 -0
  25. wbcrm/locale/de/LC_MESSAGES/django.mo +0 -0
  26. wbcrm/locale/de/LC_MESSAGES/django.po +1557 -0
  27. wbcrm/locale/de/LC_MESSAGES/django.po.translated +1630 -0
  28. wbcrm/locale/en/LC_MESSAGES/django.mo +0 -0
  29. wbcrm/locale/en/LC_MESSAGES/django.po +1466 -0
  30. wbcrm/locale/fr/LC_MESSAGES/django.mo +0 -0
  31. wbcrm/locale/fr/LC_MESSAGES/django.po +1467 -0
  32. wbcrm/migrations/0001_initial_squashed_squashed_0032_productcompanyrelationship_alter_product_prospects_and_more.py +3948 -0
  33. wbcrm/migrations/0002_alter_activity_repeat_choice.py +32 -0
  34. wbcrm/migrations/0003_remove_activity_external_id_and_more.py +63 -0
  35. wbcrm/migrations/0004_alter_activity_status.py +28 -0
  36. wbcrm/migrations/0005_account_accountrole_accountroletype_and_more.py +182 -0
  37. wbcrm/migrations/0006_alter_activity_location.py +17 -0
  38. wbcrm/migrations/0007_alter_account_status.py +23 -0
  39. wbcrm/migrations/0008_alter_activity_options.py +16 -0
  40. wbcrm/migrations/0009_alter_account_is_public.py +19 -0
  41. wbcrm/migrations/0010_alter_account_reference_id.py +17 -0
  42. wbcrm/migrations/0011_activity_summary.py +22 -0
  43. wbcrm/migrations/0012_alter_activity_summary.py +17 -0
  44. wbcrm/migrations/0013_account_action_plan_account_relationship_status_and_more.py +34 -0
  45. wbcrm/migrations/0014_alter_account_relationship_status.py +24 -0
  46. wbcrm/migrations/0015_alter_activity_type.py +23 -0
  47. wbcrm/migrations/0016_auto_20241205_1015.py +106 -0
  48. wbcrm/migrations/0017_event.py +40 -0
  49. wbcrm/migrations/0018_activity_search_vector.py +24 -0
  50. wbcrm/migrations/__init__.py +0 -0
  51. wbcrm/models/__init__.py +5 -0
  52. wbcrm/models/accounts.py +648 -0
  53. wbcrm/models/activities.py +1419 -0
  54. wbcrm/models/events.py +15 -0
  55. wbcrm/models/groups.py +119 -0
  56. wbcrm/models/llm/activity_summaries.py +41 -0
  57. wbcrm/models/llm/analyze_relationship.py +50 -0
  58. wbcrm/models/products.py +86 -0
  59. wbcrm/models/recurrence.py +280 -0
  60. wbcrm/preferences.py +13 -0
  61. wbcrm/report/activity_report.py +110 -0
  62. wbcrm/serializers/__init__.py +23 -0
  63. wbcrm/serializers/accounts.py +141 -0
  64. wbcrm/serializers/activities.py +525 -0
  65. wbcrm/serializers/groups.py +30 -0
  66. wbcrm/serializers/products.py +58 -0
  67. wbcrm/serializers/recurrence.py +91 -0
  68. wbcrm/serializers/signals.py +71 -0
  69. wbcrm/static/wbcrm/markdown/documentation/activity.md +86 -0
  70. wbcrm/static/wbcrm/markdown/documentation/activitytype.md +20 -0
  71. wbcrm/static/wbcrm/markdown/documentation/group.md +2 -0
  72. wbcrm/static/wbcrm/markdown/documentation/product.md +11 -0
  73. wbcrm/synchronization/__init__.py +0 -0
  74. wbcrm/synchronization/activity/__init__.py +0 -0
  75. wbcrm/synchronization/activity/admin.py +73 -0
  76. wbcrm/synchronization/activity/backend.py +214 -0
  77. wbcrm/synchronization/activity/backends/__init__.py +0 -0
  78. wbcrm/synchronization/activity/backends/google/__init__.py +2 -0
  79. wbcrm/synchronization/activity/backends/google/google_calendar_backend.py +406 -0
  80. wbcrm/synchronization/activity/backends/google/request_utils/__init__.py +16 -0
  81. wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/create.py +75 -0
  82. wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/delete.py +78 -0
  83. wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/update.py +155 -0
  84. wbcrm/synchronization/activity/backends/google/request_utils/internal_to_external/update.py +181 -0
  85. wbcrm/synchronization/activity/backends/google/tasks.py +21 -0
  86. wbcrm/synchronization/activity/backends/google/tests/__init__.py +0 -0
  87. wbcrm/synchronization/activity/backends/google/tests/conftest.py +1 -0
  88. wbcrm/synchronization/activity/backends/google/tests/test_data.py +81 -0
  89. wbcrm/synchronization/activity/backends/google/tests/test_google_backend.py +319 -0
  90. wbcrm/synchronization/activity/backends/google/tests/test_utils.py +274 -0
  91. wbcrm/synchronization/activity/backends/google/typing_informations.py +139 -0
  92. wbcrm/synchronization/activity/backends/google/utils.py +217 -0
  93. wbcrm/synchronization/activity/backends/outlook/__init__.py +0 -0
  94. wbcrm/synchronization/activity/backends/outlook/backend.py +593 -0
  95. wbcrm/synchronization/activity/backends/outlook/msgraph.py +436 -0
  96. wbcrm/synchronization/activity/backends/outlook/parser.py +432 -0
  97. wbcrm/synchronization/activity/backends/outlook/tests/__init__.py +0 -0
  98. wbcrm/synchronization/activity/backends/outlook/tests/conftest.py +1 -0
  99. wbcrm/synchronization/activity/backends/outlook/tests/fixtures.py +606 -0
  100. wbcrm/synchronization/activity/backends/outlook/tests/test_admin.py +118 -0
  101. wbcrm/synchronization/activity/backends/outlook/tests/test_backend.py +274 -0
  102. wbcrm/synchronization/activity/backends/outlook/tests/test_controller.py +249 -0
  103. wbcrm/synchronization/activity/backends/outlook/tests/test_parser.py +174 -0
  104. wbcrm/synchronization/activity/controller.py +627 -0
  105. wbcrm/synchronization/activity/dynamic_preferences_registry.py +119 -0
  106. wbcrm/synchronization/activity/preferences.py +27 -0
  107. wbcrm/synchronization/activity/shortcuts.py +16 -0
  108. wbcrm/synchronization/activity/tasks.py +21 -0
  109. wbcrm/synchronization/activity/urls.py +7 -0
  110. wbcrm/synchronization/activity/utils.py +46 -0
  111. wbcrm/synchronization/activity/views.py +41 -0
  112. wbcrm/synchronization/admin.py +1 -0
  113. wbcrm/synchronization/apps.py +14 -0
  114. wbcrm/synchronization/dynamic_preferences_registry.py +1 -0
  115. wbcrm/synchronization/management.py +36 -0
  116. wbcrm/synchronization/tasks.py +1 -0
  117. wbcrm/synchronization/urls.py +5 -0
  118. wbcrm/tasks.py +264 -0
  119. wbcrm/templates/email/activity.html +98 -0
  120. wbcrm/templates/email/activity_report.html +6 -0
  121. wbcrm/templates/email/daily_summary.html +72 -0
  122. wbcrm/templates/email/global_daily_summary.html +85 -0
  123. wbcrm/tests/__init__.py +0 -0
  124. wbcrm/tests/accounts/__init__.py +0 -0
  125. wbcrm/tests/accounts/test_models.py +393 -0
  126. wbcrm/tests/accounts/test_viewsets.py +88 -0
  127. wbcrm/tests/conftest.py +76 -0
  128. wbcrm/tests/disable_signals.py +62 -0
  129. wbcrm/tests/e2e/__init__.py +1 -0
  130. wbcrm/tests/e2e/e2e_wbcrm_utility.py +83 -0
  131. wbcrm/tests/e2e/test_e2e.py +370 -0
  132. wbcrm/tests/test_assignee_methods.py +40 -0
  133. wbcrm/tests/test_chartviewsets.py +112 -0
  134. wbcrm/tests/test_dto.py +64 -0
  135. wbcrm/tests/test_filters.py +52 -0
  136. wbcrm/tests/test_models.py +217 -0
  137. wbcrm/tests/test_recurrence.py +292 -0
  138. wbcrm/tests/test_report.py +21 -0
  139. wbcrm/tests/test_serializers.py +171 -0
  140. wbcrm/tests/test_tasks.py +95 -0
  141. wbcrm/tests/test_viewsets.py +967 -0
  142. wbcrm/tests/tests.py +121 -0
  143. wbcrm/typings.py +109 -0
  144. wbcrm/urls.py +67 -0
  145. wbcrm/viewsets/__init__.py +22 -0
  146. wbcrm/viewsets/accounts.py +122 -0
  147. wbcrm/viewsets/activities.py +341 -0
  148. wbcrm/viewsets/buttons/__init__.py +7 -0
  149. wbcrm/viewsets/buttons/accounts.py +27 -0
  150. wbcrm/viewsets/buttons/activities.py +89 -0
  151. wbcrm/viewsets/buttons/signals.py +17 -0
  152. wbcrm/viewsets/display/__init__.py +12 -0
  153. wbcrm/viewsets/display/accounts.py +110 -0
  154. wbcrm/viewsets/display/activities.py +444 -0
  155. wbcrm/viewsets/display/groups.py +22 -0
  156. wbcrm/viewsets/display/products.py +105 -0
  157. wbcrm/viewsets/endpoints/__init__.py +8 -0
  158. wbcrm/viewsets/endpoints/accounts.py +25 -0
  159. wbcrm/viewsets/endpoints/activities.py +30 -0
  160. wbcrm/viewsets/endpoints/groups.py +7 -0
  161. wbcrm/viewsets/endpoints/products.py +9 -0
  162. wbcrm/viewsets/groups.py +38 -0
  163. wbcrm/viewsets/menu/__init__.py +8 -0
  164. wbcrm/viewsets/menu/accounts.py +18 -0
  165. wbcrm/viewsets/menu/activities.py +49 -0
  166. wbcrm/viewsets/menu/groups.py +16 -0
  167. wbcrm/viewsets/menu/products.py +20 -0
  168. wbcrm/viewsets/mixins.py +35 -0
  169. wbcrm/viewsets/previews/__init__.py +1 -0
  170. wbcrm/viewsets/previews/activities.py +10 -0
  171. wbcrm/viewsets/products.py +57 -0
  172. wbcrm/viewsets/recurrence.py +27 -0
  173. wbcrm/viewsets/titles/__init__.py +13 -0
  174. wbcrm/viewsets/titles/accounts.py +23 -0
  175. wbcrm/viewsets/titles/activities.py +61 -0
  176. wbcrm/viewsets/titles/products.py +13 -0
  177. wbcrm/viewsets/titles/utils.py +46 -0
  178. wbcrm/workflows/__init__.py +1 -0
  179. wbcrm/workflows/assignee_methods.py +25 -0
  180. wbcrm-1.56.8.dist-info/METADATA +11 -0
  181. wbcrm-1.56.8.dist-info/RECORD +182 -0
  182. 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
+ )