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,171 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
|
|
3
|
+
from django.db.models import Q
|
|
4
|
+
from django.db.models.expressions import F
|
|
5
|
+
from django.db.models.query import QuerySet
|
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
from wbcore import serializers
|
|
8
|
+
from wbcore.contrib.directory.models import Person
|
|
9
|
+
from wbcore.serializers.serializers import Serializer
|
|
10
|
+
from wbcrm.models import Activity, ActivityParticipant, ActivityType
|
|
11
|
+
from wbcrm.serializers import ActivityTypeRepresentationSerializer
|
|
12
|
+
from wbhuman_resources.models.kpi import KPI, KPIHandler
|
|
13
|
+
from wbhuman_resources.serializers import KPIModelSerializer
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NumberOfActivityKPISerializer(KPIModelSerializer):
|
|
17
|
+
activity_type = serializers.PrimaryKeyRelatedField(
|
|
18
|
+
required=True, many=True, queryset=ActivityType.objects.all(), label=_("Activity Type")
|
|
19
|
+
)
|
|
20
|
+
_activity_type = ActivityTypeRepresentationSerializer(source="activity_type", many=True)
|
|
21
|
+
|
|
22
|
+
activity_area = serializers.ChoiceField(
|
|
23
|
+
default="all",
|
|
24
|
+
choices=[("only_internal", _("Only Internal")), ("only_external", _("Only External")), ("all", _("All"))],
|
|
25
|
+
)
|
|
26
|
+
person_participates = serializers.BooleanField(
|
|
27
|
+
default=True,
|
|
28
|
+
label=_("Participants of Activity"),
|
|
29
|
+
help_text=_("Activities considered are related to the participants"),
|
|
30
|
+
)
|
|
31
|
+
person_created = serializers.BooleanField(
|
|
32
|
+
default=True, label=_("Creator of activity"), help_text=_("Activities considered are related to the creator")
|
|
33
|
+
)
|
|
34
|
+
person_assigned = serializers.BooleanField(
|
|
35
|
+
default=True,
|
|
36
|
+
label=_("Person Assigned"),
|
|
37
|
+
help_text=_("Activities considered are related to the persons assigned"),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def update(self, instance, validated_data):
|
|
41
|
+
activity_type = validated_data.get(
|
|
42
|
+
"activity_type",
|
|
43
|
+
instance.additional_data["serializer_data"].get(
|
|
44
|
+
"activity_type", list(ActivityType.objects.values_list("id", flat=True))
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
activity_area = validated_data.get(
|
|
48
|
+
"activity_area",
|
|
49
|
+
instance.additional_data["serializer_data"].get("activity_area", "all"),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
person_participates = validated_data.get(
|
|
53
|
+
"person_participates",
|
|
54
|
+
instance.additional_data["serializer_data"].get("person_participates", True),
|
|
55
|
+
)
|
|
56
|
+
person_created = validated_data.get(
|
|
57
|
+
"person_created",
|
|
58
|
+
instance.additional_data["serializer_data"].get("person_created", True),
|
|
59
|
+
)
|
|
60
|
+
person_assigned = validated_data.get(
|
|
61
|
+
"person_assigned",
|
|
62
|
+
instance.additional_data["serializer_data"].get("person_assigned", True),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
additional_data = instance.additional_data
|
|
66
|
+
additional_data["serializer_data"]["activity_type"] = (
|
|
67
|
+
[_type.id for _type in validated_data.get("activity_type")]
|
|
68
|
+
if validated_data.get("activity_type")
|
|
69
|
+
else activity_type
|
|
70
|
+
)
|
|
71
|
+
additional_data["serializer_data"]["activity_area"] = activity_area
|
|
72
|
+
additional_data["serializer_data"]["person_participates"] = person_participates
|
|
73
|
+
additional_data["serializer_data"]["person_created"] = person_created
|
|
74
|
+
additional_data["serializer_data"]["person_assigned"] = person_assigned
|
|
75
|
+
|
|
76
|
+
additional_data["list_data"] = instance.get_handler().get_list_data(additional_data["serializer_data"])
|
|
77
|
+
validated_data["additional_data"] = additional_data
|
|
78
|
+
|
|
79
|
+
return super().update(instance, validated_data)
|
|
80
|
+
|
|
81
|
+
class Meta(KPIModelSerializer.Meta):
|
|
82
|
+
fields = (
|
|
83
|
+
*KPIModelSerializer.Meta.fields,
|
|
84
|
+
"activity_type",
|
|
85
|
+
"_activity_type",
|
|
86
|
+
"activity_area",
|
|
87
|
+
"person_participates",
|
|
88
|
+
"person_created",
|
|
89
|
+
"person_assigned",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class NumberOfActivityKPI(KPIHandler):
|
|
94
|
+
def get_name(self) -> str:
|
|
95
|
+
return _("Number of Activities")
|
|
96
|
+
|
|
97
|
+
def get_serializer(self) -> Type[Serializer]:
|
|
98
|
+
return NumberOfActivityKPISerializer
|
|
99
|
+
|
|
100
|
+
def annotate_parameters(self, queryset: QuerySet[KPI]) -> QuerySet[KPI]:
|
|
101
|
+
return queryset.annotate(
|
|
102
|
+
activity_type=F("additional_data__serializer_data__activity_type"),
|
|
103
|
+
activity_area=F("additional_data__serializer_data__activity_area"),
|
|
104
|
+
person_participates=F("additional_data__serializer_data__person_participates"),
|
|
105
|
+
person_created=F("additional_data__serializer_data__person_created"),
|
|
106
|
+
person_assigned=F("additional_data__serializer_data__person_assigned"),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def get_list_data(self, serializer_data: dict) -> list[str]:
|
|
110
|
+
activity_types = list(
|
|
111
|
+
ActivityType.objects.filter(pk__in=serializer_data["activity_type"]).values_list("title", flat=True)
|
|
112
|
+
)
|
|
113
|
+
return [
|
|
114
|
+
_("Activity Type: {types}").format(types=activity_types),
|
|
115
|
+
_("Activity Area: {area}").format(area=serializer_data["activity_area"]),
|
|
116
|
+
_("Person Participates: {participating}").format(participating={serializer_data["person_participates"]}),
|
|
117
|
+
_("Person Created: {created}").format(created=serializer_data["person_created"]),
|
|
118
|
+
_("Person Assigned: {assigned}").format(assigned=serializer_data["person_assigned"]),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
def get_display_grid(self) -> list[list[str]]:
|
|
122
|
+
return [
|
|
123
|
+
["activity_type"] * 3,
|
|
124
|
+
["activity_area"] * 3,
|
|
125
|
+
["person_created", "person_assigned", "person_participates"],
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
def evaluate(self, kpi: "KPI", evaluated_person=None, evaluation_date=None) -> int:
|
|
129
|
+
persons = (
|
|
130
|
+
[evaluated_person.id] if evaluated_person else kpi.evaluated_persons.all().values_list("id", flat=True)
|
|
131
|
+
)
|
|
132
|
+
serializer_data = kpi.additional_data.get("serializer_data")
|
|
133
|
+
qs = Activity.objects.filter(
|
|
134
|
+
period__startswith__date__gte=kpi.period.lower,
|
|
135
|
+
period__endswith__date__lte=evaluation_date if evaluation_date else kpi.period.upper,
|
|
136
|
+
).exclude(status=Activity.Status.CANCELLED)
|
|
137
|
+
qs = (
|
|
138
|
+
qs.filter(type__id__in=serializer_data.get("activity_type"))
|
|
139
|
+
if serializer_data.get("activity_type")
|
|
140
|
+
else qs
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
condition = None
|
|
144
|
+
if serializer_data.get("person_created") or serializer_data.get("person_created") is None:
|
|
145
|
+
condition = Q(creator__in=persons)
|
|
146
|
+
if serializer_data.get("person_assigned") or serializer_data.get("person_assigned") is None:
|
|
147
|
+
condition = (condition | Q(assigned_to__in=persons)) if condition else Q(assigned_to__in=persons)
|
|
148
|
+
if serializer_data.get("person_participates") or serializer_data.get("person_participates") is None:
|
|
149
|
+
condition = (condition | Q(participants__in=persons)) if condition else Q(participants__in=persons)
|
|
150
|
+
if condition:
|
|
151
|
+
qs = qs.filter(condition)
|
|
152
|
+
|
|
153
|
+
if (activity_area := serializer_data.get("activity_area")) and (activity_area != "all"):
|
|
154
|
+
participant_ids = set(
|
|
155
|
+
ActivityParticipant.objects.filter(activity__id__in=qs.values_list("id", flat=True)).values_list(
|
|
156
|
+
"participant", flat=True
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
employee_ids = Person.objects.filter_only_internal().values_list("id", flat=True)
|
|
160
|
+
externals = set(
|
|
161
|
+
Person.objects.filter(Q(id__in=participant_ids) & ~Q(id__in=employee_ids)).values_list("id", flat=True)
|
|
162
|
+
)
|
|
163
|
+
if activity_area == "only_internal":
|
|
164
|
+
qs = qs.exclude(
|
|
165
|
+
Q(creator__in=externals) | Q(assigned_to__in=externals) | Q(participants__id__in=externals)
|
|
166
|
+
)
|
|
167
|
+
elif activity_area == "only_external":
|
|
168
|
+
qs = qs.filter(
|
|
169
|
+
Q(creator__in=externals) | Q(assigned_to__in=externals) | Q(participants__id__in=externals)
|
|
170
|
+
)
|
|
171
|
+
return qs.distinct("id").count()
|
|
Binary file
|