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
@@ -0,0 +1,443 @@
1
+ from typing import Optional
2
+
3
+ from django.utils.translation import gettext as _
4
+ from django.utils.translation import pgettext
5
+ from wbcore.contrib.color.enums import WBColor
6
+ from wbcore.metadata.configs import display as dp
7
+ from wbcore.metadata.configs.display.instance_display import (
8
+ Display,
9
+ Inline,
10
+ Layout,
11
+ Page,
12
+ Section,
13
+ Style,
14
+ create_simple_display,
15
+ )
16
+ from wbcore.metadata.configs.display.instance_display.operators import default
17
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
18
+ from wbcrm.models import Activity
19
+
20
+
21
+ def get_activity_legend() -> list[dp.Legend]:
22
+ """Dynamically creates the activity legend based on the activity status enum"""
23
+
24
+ legend_items = []
25
+ for status, color in Activity.Status.get_color_map():
26
+ legend_items.append(dp.LegendItem(icon=color, label=status.label, value=status.value))
27
+ return [dp.Legend(key="status", items=legend_items)]
28
+
29
+
30
+ def get_activity_list_formatting() -> list[dp.Formatting]:
31
+ """Dynamically creates the activity list formatting based on the activity status enum"""
32
+
33
+ formatting_rules = []
34
+ for status, color in Activity.Status.get_color_map():
35
+ formatting_rules.append(dp.FormattingRule(condition=("==", status.name), style={"backgroundColor": color}))
36
+
37
+ return [dp.Formatting(column="status", formatting_rules=formatting_rules)]
38
+
39
+
40
+ class ActivityTypeDisplay(DisplayViewConfig):
41
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
42
+ return dp.ListDisplay(
43
+ fields=[
44
+ dp.Field(key="title", label=_("Title")),
45
+ dp.Field(key="icon", label=_("Icon")),
46
+ dp.Field(key="color", label=_("Color")),
47
+ dp.Field(key="score", label=_("Multiplier")),
48
+ dp.Field(key="default", label=_("Is Default")),
49
+ ]
50
+ )
51
+
52
+ def get_instance_display(self) -> Display:
53
+ return create_simple_display([["default", "."], ["title", "score"], ["color", "icon"]])
54
+
55
+
56
+ class ActivityDisplay(DisplayViewConfig):
57
+ @classmethod
58
+ def _get_activity_instance_display(cls, instance: Activity) -> Display:
59
+ """Returns an activity instance's display
60
+
61
+ Args:
62
+ instance: The activity instance that will be displayed
63
+
64
+ Returns:
65
+ Display: The display instance
66
+ """
67
+
68
+ participants_section = Section(
69
+ key="participants_section",
70
+ collapsible=False,
71
+ title=_("Participants"),
72
+ display=Display(
73
+ pages=[
74
+ Page(
75
+ title=_("Participants"),
76
+ layouts={
77
+ default(): Layout(
78
+ grid_template_areas=[["participants_table"]],
79
+ inlines=[Inline(key="participants_table", endpoint="activity_participants_table")],
80
+ )
81
+ },
82
+ ),
83
+ ]
84
+ ),
85
+ )
86
+
87
+ if instance.status == Activity.Status.PLANNED:
88
+ display = Display(
89
+ pages=[
90
+ Page(
91
+ title=_("Main Information"),
92
+ layouts={
93
+ default(): Layout(
94
+ grid_template_areas=[
95
+ [
96
+ "status",
97
+ "status",
98
+ "participants_section",
99
+ "participants_section",
100
+ ],
101
+ [
102
+ "title",
103
+ "type",
104
+ "participants_section",
105
+ "participants_section",
106
+ ],
107
+ [
108
+ "period",
109
+ "all_day",
110
+ "participants_section",
111
+ "participants_section",
112
+ ],
113
+ [
114
+ "participants",
115
+ "groups",
116
+ "participants_section",
117
+ "participants_section",
118
+ ],
119
+ [
120
+ "companies",
121
+ "disable_participant_check",
122
+ "participants_section",
123
+ "participants_section",
124
+ ],
125
+ [
126
+ "description",
127
+ "description",
128
+ "result",
129
+ "result",
130
+ ],
131
+ ["summary", "summary", "summary", "summary"],
132
+ ],
133
+ grid_auto_columns="1fr",
134
+ grid_auto_rows=Style.MIN_CONTENT,
135
+ sections=(participants_section,),
136
+ ),
137
+ },
138
+ ),
139
+ Page(
140
+ title=_("Recurrence"),
141
+ layouts={
142
+ default(): Layout(grid_template_areas=[["propagate_for_all_children", "repeat_choice"]])
143
+ },
144
+ ),
145
+ Page(
146
+ title=_("Additional Information"),
147
+ layouts={
148
+ default(): Layout(
149
+ grid_template_areas=[
150
+ [
151
+ "assigned_to",
152
+ "assigned_to",
153
+ "reminder_choice",
154
+ "visibility",
155
+ ],
156
+ ["conference_room", "online_meeting", "location", "importance"],
157
+ [
158
+ "created",
159
+ "edited",
160
+ ".",
161
+ ".",
162
+ ],
163
+ ],
164
+ grid_template_columns=[
165
+ "minmax(min-content, 1fr)",
166
+ "minmax(min-content, 1fr)",
167
+ "minmax(min-content, 2fr)",
168
+ "minmax(min-content, 2fr)",
169
+ ],
170
+ grid_auto_rows=Style.MIN_CONTENT,
171
+ )
172
+ },
173
+ ),
174
+ ]
175
+ )
176
+ else:
177
+ display = Display(
178
+ pages=[
179
+ Page(
180
+ title=_("Main Information"),
181
+ layouts={
182
+ default(): Layout(
183
+ grid_template_areas=[
184
+ [
185
+ "status",
186
+ ".",
187
+ ],
188
+ [
189
+ "title",
190
+ "type",
191
+ ],
192
+ [
193
+ "period",
194
+ "all_day",
195
+ ],
196
+ [
197
+ "participants",
198
+ "groups",
199
+ ],
200
+ [
201
+ "companies",
202
+ "disable_participant_check",
203
+ ],
204
+ [
205
+ "description",
206
+ "description" if instance.status == Activity.Status.CANCELLED else "result",
207
+ ],
208
+ ["summary", "summary"],
209
+ ],
210
+ grid_auto_columns="1fr",
211
+ grid_auto_rows=Style.MIN_CONTENT,
212
+ ),
213
+ },
214
+ ),
215
+ Page(
216
+ title=_("Participant Information"),
217
+ layouts={
218
+ default(): Layout(
219
+ grid_template_areas=[["participants_section"]], sections=(participants_section,)
220
+ )
221
+ },
222
+ ),
223
+ Page(
224
+ title=_("Recurrence"),
225
+ layouts={default(): Layout(grid_template_areas=[["propagate_for_all_children"]])},
226
+ ),
227
+ Page(
228
+ title=_("Additional Information"),
229
+ layouts={
230
+ default(): Layout(
231
+ grid_template_areas=[
232
+ [
233
+ "assigned_to",
234
+ "assigned_to",
235
+ "reminder_choice",
236
+ "visibility",
237
+ ],
238
+ ["conference_room", "online_meeting", "location", "importance"],
239
+ [
240
+ "created",
241
+ "edited",
242
+ ".",
243
+ ".",
244
+ ],
245
+ ],
246
+ grid_template_columns=[
247
+ "minmax(min-content, 1fr)",
248
+ "minmax(min-content, 1fr)",
249
+ "minmax(min-content, 2fr)",
250
+ "minmax(min-content, 2fr)",
251
+ ],
252
+ grid_auto_rows=Style.MIN_CONTENT,
253
+ )
254
+ },
255
+ ),
256
+ ]
257
+ )
258
+
259
+ return display
260
+
261
+ @classmethod
262
+ def _get_new_activity_instance_display(cls) -> Display:
263
+ """Returns the display for creating a new activity
264
+
265
+ Returns:
266
+ Display: The display instance
267
+ """
268
+
269
+ new_recurrence_section = Section(
270
+ key="new_recurrence_section",
271
+ collapsible=False,
272
+ title=_("Repeat Until"),
273
+ display=Display(
274
+ pages=[
275
+ Page(
276
+ title=_("Repeat Until"),
277
+ layouts={
278
+ default(): Layout(
279
+ grid_template_areas=[["recurrence_end", "recurrence_count", "."]],
280
+ grid_template_columns=[
281
+ "minmax(min-content, 1fr)",
282
+ "minmax(min-content, 1fr)",
283
+ "minmax(min-content, 2fr)",
284
+ ],
285
+ grid_auto_rows=Style.MIN_CONTENT,
286
+ )
287
+ },
288
+ ),
289
+ ]
290
+ ),
291
+ )
292
+
293
+ return Display(
294
+ pages=[
295
+ Page(
296
+ title=_("Main Information"),
297
+ layouts={
298
+ default(): Layout(
299
+ grid_template_areas=[
300
+ [
301
+ "title",
302
+ "type",
303
+ ],
304
+ [
305
+ "period",
306
+ "all_day",
307
+ ],
308
+ [
309
+ "participants",
310
+ "groups",
311
+ ],
312
+ [
313
+ "companies",
314
+ "disable_participant_check",
315
+ ],
316
+ [
317
+ "description",
318
+ "result",
319
+ ],
320
+ ],
321
+ grid_auto_columns="1fr",
322
+ grid_auto_rows=Style.MIN_CONTENT,
323
+ ),
324
+ },
325
+ ),
326
+ Page(
327
+ title=_("Recurrence"),
328
+ layouts={
329
+ default(): Layout(
330
+ grid_template_areas=[
331
+ ["repeat_choice", "."],
332
+ ["new_recurrence_section", "new_recurrence_section"],
333
+ ],
334
+ grid_template_columns=["minmax(min-content, 1fr)", "minmax(min-content, 3fr)"],
335
+ grid_auto_rows=Style.MIN_CONTENT,
336
+ sections=(new_recurrence_section,),
337
+ )
338
+ },
339
+ ),
340
+ Page(
341
+ title=_("Additional Information"),
342
+ layouts={
343
+ default(): Layout(
344
+ grid_template_areas=[
345
+ [
346
+ "assigned_to",
347
+ "assigned_to",
348
+ "reminder_choice",
349
+ "visibility",
350
+ ],
351
+ ["conference_room", "online_meeting", "location", "importance"],
352
+ ],
353
+ grid_template_columns=[
354
+ "minmax(min-content, 1fr)",
355
+ "minmax(min-content, 1fr)",
356
+ "minmax(min-content, 2fr)",
357
+ "minmax(min-content, 2fr)",
358
+ ],
359
+ grid_auto_rows=Style.MIN_CONTENT,
360
+ )
361
+ },
362
+ ),
363
+ ]
364
+ )
365
+
366
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
367
+ return dp.ListDisplay(
368
+ fields=[
369
+ dp.Field(key="heat", label=_("Sentiment"), width=75),
370
+ dp.Field(key="type", label=_("Type")),
371
+ dp.Field(key="title", label=_("Title")),
372
+ dp.Field(key="summary", label=_("Summary")),
373
+ dp.Field(key="period", label=_("Period")),
374
+ dp.Field(key="participants", label=_("Participants")),
375
+ dp.Field(key="companies", label=_("Companies")),
376
+ dp.Field(key="groups", label=_("Groups")),
377
+ dp.Field(key="edited", label=_("Edited")),
378
+ dp.Field(key="created", label=pgettext("As a table header", "Created")),
379
+ dp.Field(key="description", label=_("Description")),
380
+ dp.Field(key="result", label=pgettext("As a table header", "Review")),
381
+ dp.Field(key="latest_reviewer", label=_("Latest Reviewer")),
382
+ ],
383
+ legends=get_activity_legend(),
384
+ formatting=get_activity_list_formatting(),
385
+ )
386
+
387
+ def get_instance_display(self) -> Display:
388
+ return (
389
+ self._get_activity_instance_display(self.view.get_object())
390
+ if "pk" in self.view.kwargs
391
+ else self._get_new_activity_instance_display()
392
+ )
393
+
394
+
395
+ class ActivityParticipantDisplayConfig(DisplayViewConfig):
396
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
397
+ return dp.ListDisplay(
398
+ fields=[
399
+ dp.Field(key="participant", label=_("Participant")),
400
+ dp.Field(key="participation_status", label=_("Participation Status")),
401
+ dp.Field(key="status_changed", label=_("Status Changed")),
402
+ dp.Field(key="position", label=_("Position")),
403
+ dp.Field(key="primary_email", label=_("Primary Email")),
404
+ dp.Field(key="primary_telephone", label=_("Primary Phone Number")),
405
+ ],
406
+ legends=[
407
+ dp.Legend(
408
+ key="is_occupied_filter",
409
+ items=[
410
+ dp.LegendItem(
411
+ icon=WBColor.GREEN_LIGHT.value,
412
+ label=_("Is Available"),
413
+ value=False,
414
+ ),
415
+ dp.LegendItem(
416
+ icon=WBColor.RED_LIGHT.value,
417
+ label=_("Is Occupied"),
418
+ value=True,
419
+ ),
420
+ ],
421
+ )
422
+ ],
423
+ formatting=[
424
+ dp.Formatting(
425
+ column="is_occupied",
426
+ formatting_rules=[
427
+ dp.FormattingRule(
428
+ style={"backgroundColor": WBColor.GREEN_LIGHT.value},
429
+ condition=("==", False),
430
+ ),
431
+ dp.FormattingRule(
432
+ style={"backgroundColor": WBColor.RED_LIGHT.value},
433
+ condition=("==", True),
434
+ ),
435
+ ],
436
+ )
437
+ ],
438
+ )
439
+
440
+ def get_instance_display(self) -> Display:
441
+ return create_simple_display(
442
+ [["participation_status", "participation_status"], ["participant", "customer_status"]]
443
+ )
@@ -0,0 +1,22 @@
1
+ from typing import Optional
2
+
3
+ from django.utils.translation import gettext as _
4
+ from wbcore.metadata.configs import display as dp
5
+ from wbcore.metadata.configs.display.instance_display import Display
6
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
7
+ create_simple_display,
8
+ )
9
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
10
+
11
+
12
+ class GroupModelDisplay(DisplayViewConfig):
13
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
14
+ return dp.ListDisplay(
15
+ fields=(
16
+ dp.Field(key="title", label=_("Name")),
17
+ dp.Field(key="members", label=_("Members")),
18
+ ),
19
+ )
20
+
21
+ def get_instance_display(self) -> Display:
22
+ return create_simple_display([["title"], ["members"]])
@@ -0,0 +1,105 @@
1
+ from typing import Optional
2
+
3
+ from django.utils.translation import gettext as _
4
+ from wbcore.contrib.directory.viewsets.display import CompanyModelDisplay
5
+ from wbcore.dispatch import receiver_all_subclasses
6
+ from wbcore.metadata.configs import display as dp
7
+ from wbcore.metadata.configs.display import add_display_pages
8
+ from wbcore.metadata.configs.display.instance_display import (
9
+ Display,
10
+ Inline,
11
+ Layout,
12
+ Page,
13
+ Section,
14
+ Style,
15
+ )
16
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
17
+ create_simple_display,
18
+ default,
19
+ )
20
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
21
+
22
+
23
+ class ProductDisplay(DisplayViewConfig):
24
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
25
+ return dp.ListDisplay(
26
+ fields=(
27
+ dp.Field(key="title", label=_("Title")),
28
+ dp.Field(key="is_competitor", label=_("Is Competitor")),
29
+ dp.Field(key="prospects", label=_("Prospects")),
30
+ )
31
+ )
32
+
33
+ def get_instance_display(self) -> Display:
34
+ return Display(
35
+ pages=[
36
+ Page(
37
+ layouts={
38
+ default(): Layout(
39
+ grid_template_areas=[["title", "is_competitor", "prospects"]],
40
+ grid_template_columns=[
41
+ "minmax(min-content, 0.5fr)",
42
+ "minmax(min-content, 1fr)",
43
+ "minmax(min-content, 1fr)",
44
+ ],
45
+ grid_auto_rows=Style.MIN_CONTENT,
46
+ column_gap=Style.rem(6),
47
+ ),
48
+ },
49
+ ),
50
+ ]
51
+ )
52
+
53
+
54
+ class ProductCompanyRelationshipDisplay(DisplayViewConfig):
55
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
56
+ return dp.ListDisplay(
57
+ fields=(
58
+ dp.Field(key="product", label=_("Product")),
59
+ dp.Field(key="competitor_product", label=_("Is Competitor")),
60
+ )
61
+ )
62
+
63
+ def get_instance_display(self) -> Display:
64
+ return create_simple_display([["product"]])
65
+
66
+
67
+ @receiver_all_subclasses(add_display_pages, sender=CompanyModelDisplay)
68
+ def add_interested_product_page(sender, *args, **kwargs):
69
+ return [
70
+ Page(
71
+ title=_("Interests"),
72
+ layouts={
73
+ default(): Layout(
74
+ grid_template_areas=[["interested_products_section"]],
75
+ grid_auto_columns="minmax(min-content, 1fr)",
76
+ grid_auto_rows=Style.MIN_CONTENT,
77
+ sections=[
78
+ Section(
79
+ key="interested_products_section",
80
+ title=_("Interested Products"),
81
+ collapsible=False,
82
+ display=Display(
83
+ pages=[
84
+ Page(
85
+ title=_("Interested Products"),
86
+ layouts={
87
+ default(): Layout(
88
+ grid_template_areas=[["interested_products_table"]],
89
+ inlines=[
90
+ Inline(
91
+ key="interested_products_table",
92
+ endpoint="interested_products",
93
+ )
94
+ ],
95
+ )
96
+ },
97
+ ),
98
+ ]
99
+ ),
100
+ )
101
+ ],
102
+ ),
103
+ },
104
+ )
105
+ ]
@@ -0,0 +1,8 @@
1
+ from .accounts import (
2
+ AccountRoleAccountEndpointConfig,
3
+ ChildAccountAccountEndpointConfig,
4
+ InheritedAccountRoleAccountEndpointConfig,
5
+ )
6
+ from .activities import ActivityEndpointConfig, ActivityParticipantModelEndpointConfig
7
+ from .groups import GroupEndpointConfig
8
+ from .products import ProductCompanyRelationshipEndpointConfig
@@ -0,0 +1,32 @@
1
+ from rest_framework.reverse import reverse
2
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
3
+
4
+
5
+ class ChildAccountAccountEndpointConfig(EndpointViewConfig):
6
+ def get_endpoint(self, **kwargs):
7
+ return reverse(
8
+ "wbcrm:account-childaccount-list",
9
+ args=[self.view.kwargs["account_id"]],
10
+ request=self.request,
11
+ )
12
+
13
+
14
+ class AccountRoleAccountEndpointConfig(EndpointViewConfig):
15
+ def get_endpoint(self, **kwargs):
16
+ return reverse(
17
+ "wbcrm:account-accountrole-list",
18
+ args=[self.view.kwargs["account_id"]],
19
+ request=self.request,
20
+ )
21
+
22
+
23
+ class InheritedAccountRoleAccountEndpointConfig(EndpointViewConfig):
24
+ def get_endpoint(self, **kwargs):
25
+ return None
26
+
27
+ def get_list_endpoint(self, **kwargs):
28
+ return reverse(
29
+ "wbcrm:account-inheritedrole-list",
30
+ args=[self.view.kwargs["account_id"]],
31
+ request=self.request,
32
+ )
@@ -0,0 +1,30 @@
1
+ from rest_framework.reverse import reverse
2
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
3
+
4
+
5
+ class ActivityEndpointConfig(EndpointViewConfig):
6
+ def get_create_endpoint(self, **kwargs):
7
+ user_id = self.request.user.profile.id
8
+ base_url = "wbcrm:activity-list"
9
+ filter_url = f"?participants={user_id}"
10
+
11
+ if participants := self.request.GET.get("participants", None):
12
+ filter_url += f",{participants}"
13
+ if companies := self.request.GET.get("companies", None):
14
+ filter_url += f"&companies={companies}"
15
+ return f"{reverse(base_url, args=[], request=self.request)}{filter_url}"
16
+
17
+ def get_delete_endpoint(self, **kwargs):
18
+ if "pk" in self.view.kwargs and (self.view.is_private_for_user or self.view.is_confidential_for_user):
19
+ return None
20
+ return super().get_delete_endpoint(**kwargs)
21
+
22
+
23
+ class ActivityParticipantModelEndpointConfig(EndpointViewConfig):
24
+ def get_endpoint(self, **kwargs):
25
+ return reverse("wbcrm:activity-participant-list", args=[self.view.kwargs["activity_id"]], request=self.request)
26
+
27
+ def get_create_endpoint(self, **kwargs):
28
+ if "activity_id" in self.view.kwargs:
29
+ return f"{super().get_create_endpoint(**kwargs)}?activity_id={self.view.kwargs['activity_id']}"
30
+ return super().get_create_endpoint(**kwargs)
@@ -0,0 +1,7 @@
1
+ from rest_framework.reverse import reverse
2
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
3
+
4
+
5
+ class GroupEndpointConfig(EndpointViewConfig):
6
+ def get_pre_change_endpoint(self, pk):
7
+ return reverse("wbcrm:group-pre-change", args=[pk], request=self.request)
@@ -0,0 +1,9 @@
1
+ from rest_framework.reverse import reverse
2
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
3
+
4
+
5
+ class ProductCompanyRelationshipEndpointConfig(EndpointViewConfig):
6
+ def get_endpoint(self, **kwargs):
7
+ return reverse(
8
+ "wbcrm:company-interestedproduct-list", args=[self.view.kwargs["company_id"]], request=self.request
9
+ )