wbcore 1.46.0__py2.py3-none-any.whl → 1.58.2__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 (321) hide show
  1. wbcore/cache/decorators.py +5 -3
  2. wbcore/cache/registry.py +14 -7
  3. wbcore/configs/__init__.py +1 -0
  4. wbcore/configs/configs.py +5 -0
  5. wbcore/configs/decorators.py +1 -1
  6. wbcore/configurations/configurations/apps.py +3 -2
  7. wbcore/configurations/configurations/authentication.py +1 -1
  8. wbcore/configurations/configurations/base.py +1 -1
  9. wbcore/configurations/configurations/cache.py +1 -1
  10. wbcore/configurations/configurations/i18nl10n.py +2 -1
  11. wbcore/configurations/configurations/maintenance.py +1 -1
  12. wbcore/configurations/configurations/media.py +1 -1
  13. wbcore/configurations/configurations/middleware.py +1 -1
  14. wbcore/configurations/configurations/rest_framework.py +1 -1
  15. wbcore/configurations/configurations/static.py +3 -3
  16. wbcore/configurations/configurations/wbcore.py +1 -1
  17. wbcore/content_type/serializers.py +13 -5
  18. wbcore/content_type/utils.py +3 -3
  19. wbcore/content_type/viewsets.py +2 -2
  20. wbcore/contrib/agenda/filters/calendar_item.py +5 -4
  21. wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po +145 -52
  22. wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po.translated +236 -0
  23. wbcore/contrib/agenda/locale/en/LC_MESSAGES/django.po +200 -0
  24. wbcore/contrib/agenda/locale/fr/LC_MESSAGES/django.po +201 -0
  25. wbcore/contrib/agenda/viewsets/calendar_items.py +7 -7
  26. wbcore/contrib/agenda/viewsets/menu/calendar_items.py +0 -6
  27. wbcore/contrib/ai/exceptions.py +5 -5
  28. wbcore/contrib/ai/llm/config.py +76 -27
  29. wbcore/contrib/ai/llm/mixins.py +5 -8
  30. wbcore/contrib/ai/llm/utils.py +50 -26
  31. wbcore/contrib/authentication/admin.py +2 -2
  32. wbcore/contrib/authentication/factories/__init__.py +8 -1
  33. wbcore/contrib/authentication/factories/users.py +19 -0
  34. wbcore/contrib/authentication/filters.py +1 -2
  35. wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po +209 -187
  36. wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po.translated +634 -0
  37. wbcore/contrib/authentication/locale/en/LC_MESSAGES/django.po +590 -0
  38. wbcore/contrib/authentication/locale/fr/LC_MESSAGES/django.po +592 -0
  39. wbcore/contrib/authentication/models/users.py +3 -3
  40. wbcore/contrib/authentication/models/users_activities.py +1 -1
  41. wbcore/contrib/authentication/serializers/users.py +2 -2
  42. wbcore/contrib/authentication/tests/test_tokens.py +3 -3
  43. wbcore/contrib/authentication/tests/test_users.py +0 -1
  44. wbcore/contrib/authentication/urls.py +0 -4
  45. wbcore/contrib/authentication/viewsets/endpoints/user_activities.py +2 -11
  46. wbcore/contrib/authentication/viewsets/endpoints/users.py +0 -3
  47. wbcore/contrib/authentication/viewsets/user_activities.py +2 -1
  48. wbcore/contrib/authentication/viewsets/users.py +6 -4
  49. wbcore/contrib/color/models.py +2 -1
  50. wbcore/contrib/currency/factories.py +1 -1
  51. wbcore/contrib/currency/import_export/backends/fixerio/currency_fx_rates.py +3 -1
  52. wbcore/contrib/currency/models.py +30 -8
  53. wbcore/contrib/currency/serializers.py +5 -1
  54. wbcore/contrib/currency/tests/test_serializers.py +7 -3
  55. wbcore/contrib/currency/tests/test_viewsets.py +1 -1
  56. wbcore/contrib/currency/viewsets/currency.py +2 -2
  57. wbcore/contrib/currency/viewsets/endpoints/currency_fx_rates.py +0 -9
  58. wbcore/contrib/dataloader/tests/test/dataloaders/protocols.py +1 -2
  59. wbcore/contrib/dataloader/utils.py +2 -2
  60. wbcore/contrib/directory/factories/__init__.py +1 -1
  61. wbcore/contrib/directory/factories/entries.py +2 -1
  62. wbcore/contrib/directory/filters/entries.py +9 -0
  63. wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po +728 -714
  64. wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po.translated +1779 -0
  65. wbcore/contrib/directory/locale/en/LC_MESSAGES/django.po +1652 -0
  66. wbcore/contrib/directory/locale/fr/LC_MESSAGES/django.po +1654 -0
  67. wbcore/contrib/directory/migrations/0011_person_description_person_i18n.py +24 -0
  68. wbcore/contrib/directory/migrations/0012_alter_person_managers.py +20 -0
  69. wbcore/contrib/directory/migrations/0013_alter_clientmanagerrelationship_options.py +17 -0
  70. wbcore/contrib/directory/models/contacts.py +2 -2
  71. wbcore/contrib/directory/models/entries.py +31 -5
  72. wbcore/contrib/directory/models/relationships.py +31 -35
  73. wbcore/contrib/directory/permissions.py +6 -0
  74. wbcore/contrib/directory/serializers/companies.py +16 -8
  75. wbcore/contrib/directory/serializers/contacts.py +8 -8
  76. wbcore/contrib/directory/serializers/entries.py +26 -15
  77. wbcore/contrib/directory/serializers/entry_representations.py +4 -2
  78. wbcore/contrib/directory/serializers/persons.py +12 -10
  79. wbcore/contrib/directory/serializers/relationships.py +2 -2
  80. wbcore/contrib/directory/tests/conftest.py +2 -0
  81. wbcore/contrib/directory/tests/disable_signals.py +11 -1
  82. wbcore/contrib/directory/tests/signals.py +2 -2
  83. wbcore/contrib/directory/tests/test_models.py +88 -66
  84. wbcore/contrib/directory/tests/test_serializers.py +1 -1
  85. wbcore/contrib/directory/tests/test_viewsets.py +8 -8
  86. wbcore/contrib/directory/viewsets/buttons/__init__.py +1 -1
  87. wbcore/contrib/directory/viewsets/buttons/relationships.py +32 -0
  88. wbcore/contrib/directory/viewsets/contacts.py +6 -6
  89. wbcore/contrib/directory/viewsets/display/__init__.py +1 -1
  90. wbcore/contrib/directory/viewsets/display/contacts.py +1 -14
  91. wbcore/contrib/directory/viewsets/display/entries.py +68 -38
  92. wbcore/contrib/directory/viewsets/display/relationships.py +26 -50
  93. wbcore/contrib/directory/viewsets/endpoints/relationships.py +1 -26
  94. wbcore/contrib/directory/viewsets/entries.py +8 -6
  95. wbcore/contrib/directory/viewsets/previews/entries.py +3 -3
  96. wbcore/contrib/directory/viewsets/relationships.py +16 -2
  97. wbcore/contrib/directory/viewsets/titles/relationships.py +2 -3
  98. wbcore/contrib/documents/filters.py +0 -2
  99. wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po +103 -94
  100. wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po.translated +285 -0
  101. wbcore/contrib/documents/locale/en/LC_MESSAGES/django.po +271 -0
  102. wbcore/contrib/documents/locale/fr/LC_MESSAGES/django.po +270 -0
  103. wbcore/contrib/documents/tests/test_models.py +32 -28
  104. wbcore/contrib/documents/viewsets/endpoints/shareable_links.py +2 -21
  105. wbcore/contrib/dynamic_preferences/types.py +108 -0
  106. wbcore/contrib/dynamic_preferences/viewsets.py +27 -0
  107. wbcore/contrib/example_app/filters/event.py +3 -1
  108. wbcore/contrib/example_app/filters/match.py +1 -1
  109. wbcore/contrib/example_app/models.py +91 -22
  110. wbcore/contrib/example_app/serializers/person_team.py +4 -4
  111. wbcore/contrib/example_app/templates/example_app/embedded_view.html +19 -0
  112. wbcore/contrib/example_app/tests/e2e/test_teams.py +1 -1
  113. wbcore/contrib/example_app/tests/test_models/test_match.py +17 -7
  114. wbcore/contrib/example_app/urls.py +2 -0
  115. wbcore/contrib/example_app/views.py +7 -0
  116. wbcore/contrib/example_app/viewsets/displays/team.py +23 -4
  117. wbcore/contrib/example_app/viewsets/menu/menus.py +1 -1
  118. wbcore/contrib/example_app/viewsets/menus.py +1 -1
  119. wbcore/contrib/geography/tests/conftest.py +14 -0
  120. wbcore/contrib/geography/tests/test_models.py +23 -8
  121. wbcore/contrib/geography/tests/test_viewsets.py +96 -2
  122. wbcore/contrib/guardian/tests/test_model_mixins.py +3 -4
  123. wbcore/contrib/guardian/tests/test_tasks.py +9 -9
  124. wbcore/contrib/guardian/tests/test_viewsets.py +2 -2
  125. wbcore/contrib/guardian/viewsets/configs/__init__.py +1 -1
  126. wbcore/contrib/guardian/viewsets/configs/buttons.py +9 -0
  127. wbcore/contrib/guardian/viewsets/configs/endpoints.py +7 -0
  128. wbcore/contrib/guardian/viewsets/viewsets.py +2 -0
  129. wbcore/contrib/i18n/__init__.py +2 -0
  130. wbcore/contrib/i18n/buttons.py +33 -0
  131. wbcore/contrib/i18n/serializers/__init__.py +0 -0
  132. wbcore/contrib/i18n/serializers/fields.py +20 -0
  133. wbcore/contrib/i18n/serializers/mixins.py +13 -0
  134. wbcore/contrib/i18n/tests/conftest.py +11 -0
  135. wbcore/contrib/i18n/tests/test_viewsets.py +67 -0
  136. wbcore/contrib/i18n/translation.py +140 -0
  137. wbcore/contrib/i18n/viewsets.py +36 -0
  138. wbcore/contrib/icons/backends/default.py +1 -0
  139. wbcore/contrib/icons/backends/material.py +1 -0
  140. wbcore/contrib/icons/icons.py +5 -8
  141. wbcore/contrib/io/admin.py +1 -0
  142. wbcore/contrib/io/backends/mail.py +3 -2
  143. wbcore/contrib/io/backends/utils.py +14 -17
  144. wbcore/contrib/io/exceptions.py +8 -0
  145. wbcore/contrib/io/factories.py +1 -1
  146. wbcore/contrib/io/import_export/backends/mail.py +1 -0
  147. wbcore/contrib/io/import_export/backends/sftp.py +29 -20
  148. wbcore/contrib/io/import_export/backends/stream.py +2 -2
  149. wbcore/contrib/io/import_export/parsers/__init__.py +0 -0
  150. wbcore/contrib/io/import_export/parsers/base_csv.py +36 -0
  151. wbcore/contrib/io/import_export/parsers/resources.py +50 -0
  152. wbcore/contrib/io/imports.py +33 -25
  153. wbcore/contrib/io/locale/de/LC_MESSAGES/django.po +114 -22
  154. wbcore/contrib/io/locale/de/LC_MESSAGES/django.po.translated +103 -0
  155. wbcore/contrib/io/locale/en/LC_MESSAGES/django.po +138 -0
  156. wbcore/contrib/io/locale/fr/LC_MESSAGES/django.po +138 -0
  157. wbcore/contrib/io/migrations/0008_importsource_resource_kwargs.py +18 -0
  158. wbcore/contrib/io/models.py +65 -45
  159. wbcore/contrib/io/resources.py +0 -6
  160. wbcore/contrib/io/serializers.py +2 -2
  161. wbcore/contrib/io/signals.py +4 -0
  162. wbcore/contrib/io/tests/test_backends.py +19 -13
  163. wbcore/contrib/io/tests/test_exports.py +1 -1
  164. wbcore/contrib/io/tests/test_imports.py +1 -1
  165. wbcore/contrib/io/tests/test_models.py +47 -14
  166. wbcore/contrib/io/tests/test_viewsets.py +271 -0
  167. wbcore/contrib/io/viewset_mixins.py +41 -54
  168. wbcore/contrib/notifications/admin.py +1 -0
  169. wbcore/contrib/notifications/apps.py +2 -1
  170. wbcore/contrib/notifications/backends/abstract_backend.py +2 -4
  171. wbcore/contrib/notifications/backends/firebase/backends.py +5 -2
  172. wbcore/contrib/notifications/dispatch.py +18 -7
  173. wbcore/contrib/notifications/factories/notification_types.py +1 -0
  174. wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po +25 -19
  175. wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po.translated +63 -0
  176. wbcore/contrib/notifications/locale/en/LC_MESSAGES/django.po +61 -0
  177. wbcore/contrib/notifications/locale/fr/LC_MESSAGES/django.po +62 -0
  178. wbcore/contrib/notifications/migrations/0008_notificationtype_is_lock.py +18 -0
  179. wbcore/contrib/notifications/migrations/0009_alter_notificationtypesetting_options_and_more.py +32 -0
  180. wbcore/contrib/notifications/models/notification_types.py +67 -24
  181. wbcore/contrib/notifications/serializers/notification_types.py +16 -1
  182. wbcore/contrib/notifications/tests/test_models/test_tokens.py +8 -0
  183. wbcore/contrib/notifications/tests/test_serializers/test_notification_types.py +5 -0
  184. wbcore/contrib/notifications/tests/test_viewsets/test_notification_types.py +3 -5
  185. wbcore/contrib/notifications/utils.py +3 -2
  186. wbcore/contrib/notifications/viewsets/configs/notification_types.py +28 -6
  187. wbcore/contrib/notifications/viewsets/menus.py +1 -1
  188. wbcore/contrib/notifications/viewsets/notification_types.py +12 -2
  189. wbcore/contrib/pandas/fields.py +38 -10
  190. wbcore/contrib/pandas/filters.py +4 -1
  191. wbcore/contrib/pandas/filterset.py +8 -7
  192. wbcore/contrib/pandas/tests/test_fields/test_number_fields.py +2 -7
  193. wbcore/contrib/pandas/utils.py +1 -1
  194. wbcore/contrib/pandas/views.py +14 -13
  195. wbcore/contrib/tags/models/tags.py +4 -1
  196. wbcore/contrib/workflow/factories/display.py +2 -2
  197. wbcore/contrib/workflow/factories/transition.py +16 -15
  198. wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po +457 -566
  199. wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po.translated +1326 -0
  200. wbcore/contrib/workflow/locale/en/LC_MESSAGES/django.po +1102 -0
  201. wbcore/contrib/workflow/locale/fr/LC_MESSAGES/django.po +1114 -0
  202. wbcore/contrib/workflow/models/data.py +7 -4
  203. wbcore/contrib/workflow/models/process.py +2 -2
  204. wbcore/contrib/workflow/models/step.py +57 -15
  205. wbcore/contrib/workflow/serializers/data.py +8 -8
  206. wbcore/contrib/workflow/serializers/process.py +3 -2
  207. wbcore/contrib/workflow/tests/conftest.py +224 -0
  208. wbcore/contrib/workflow/tests/test_dispatch.py +82 -77
  209. wbcore/contrib/workflow/tests/test_displays.py +10 -88
  210. wbcore/contrib/workflow/tests/test_filters.py +57 -40
  211. wbcore/contrib/workflow/tests/test_models/step/test_decision_step.py +71 -68
  212. wbcore/contrib/workflow/tests/test_models/step/test_email_step.py +78 -38
  213. wbcore/contrib/workflow/tests/test_models/step/test_finish_step.py +152 -90
  214. wbcore/contrib/workflow/tests/test_models/step/test_join_step.py +100 -110
  215. wbcore/contrib/workflow/tests/test_models/step/test_step.py +168 -33
  216. wbcore/contrib/workflow/tests/test_models/test_condition.py +1 -1
  217. wbcore/contrib/workflow/tests/test_models/test_workflow.py +3 -3
  218. wbcore/contrib/workflow/tests/test_serializers.py +172 -150
  219. wbcore/contrib/workflow/tests/test_viewsets.py +264 -323
  220. wbcore/contrib/workflow/tests/test_workflow_assignees.py +215 -205
  221. wbcore/contrib/workflow/viewsets/process.py +4 -1
  222. wbcore/contrib/workflow/workflows/assignees.py +12 -7
  223. wbcore/dynamic_preferences_registry.py +102 -0
  224. wbcore/enums.py +2 -51
  225. wbcore/filters/fields/choices.py +4 -6
  226. wbcore/filters/fields/content_type.py +15 -4
  227. wbcore/filters/fields/datetime.py +50 -25
  228. wbcore/filters/fields/models.py +18 -9
  229. wbcore/filters/fields/numbers.py +9 -8
  230. wbcore/filters/filterset.py +27 -6
  231. wbcore/filters/mixins.py +41 -42
  232. wbcore/forms.py +6 -6
  233. wbcore/fsm/markdown_extensions.py +1 -1
  234. wbcore/fsm/mixins.py +20 -6
  235. wbcore/locale/de/LC_MESSAGES/django.po +982 -397
  236. wbcore/locale/de/LC_MESSAGES/django.po.translated +1580 -0
  237. wbcore/locale/en/LC_MESSAGES/django.po +1234 -0
  238. wbcore/locale/fr/LC_MESSAGES/django.po +1235 -0
  239. wbcore/markdown/models.py +8 -5
  240. wbcore/markdown/views.py +1 -1
  241. wbcore/menus/menus.py +2 -2
  242. wbcore/metadata/configs/buttons/bases.py +10 -7
  243. wbcore/metadata/configs/buttons/buttons.py +2 -1
  244. wbcore/metadata/configs/buttons/enums.py +50 -0
  245. wbcore/metadata/configs/buttons/view_config.py +13 -46
  246. wbcore/metadata/configs/display/display.py +2 -2
  247. wbcore/metadata/configs/display/formatting.py +6 -9
  248. wbcore/metadata/configs/display/instance_display/display.py +5 -2
  249. wbcore/metadata/configs/display/instance_display/pages.py +1 -1
  250. wbcore/metadata/configs/display/instance_display/shortcuts.py +1 -1
  251. wbcore/metadata/configs/display/list_display.py +54 -40
  252. wbcore/metadata/configs/display/models.py +6 -0
  253. wbcore/metadata/configs/display/view_config.py +11 -9
  254. wbcore/metadata/configs/endpoints.py +11 -4
  255. wbcore/metadata/configs/fields.py +6 -1
  256. wbcore/metadata/configs/filter_fields.py +12 -13
  257. wbcore/metadata/configs/identifiers.py +3 -1
  258. wbcore/metadata/tests/test_buttons.py +13 -16
  259. wbcore/models/fields.py +2 -2
  260. wbcore/pagination.py +1 -2
  261. wbcore/permissions/permissions.py +2 -2
  262. wbcore/permissions/utils.py +2 -2
  263. wbcore/release_notes/display.py +2 -8
  264. wbcore/release_notes/serializers.py +2 -9
  265. wbcore/release_notes/viewsets.py +8 -2
  266. wbcore/reversion/viewsets/titles.py +4 -3
  267. wbcore/serializers/__init__.py +2 -0
  268. wbcore/serializers/fields/__init__.py +2 -1
  269. wbcore/serializers/fields/boolean.py +1 -1
  270. wbcore/serializers/fields/choice.py +28 -4
  271. wbcore/serializers/fields/datetime.py +45 -36
  272. wbcore/serializers/fields/fields.py +1 -1
  273. wbcore/serializers/fields/fsm.py +1 -1
  274. wbcore/serializers/fields/list.py +2 -5
  275. wbcore/serializers/fields/mixins.py +24 -11
  276. wbcore/serializers/fields/number.py +6 -23
  277. wbcore/serializers/fields/other.py +2 -10
  278. wbcore/serializers/fields/related.py +4 -6
  279. wbcore/serializers/fields/text.py +1 -1
  280. wbcore/serializers/fields/types.py +2 -0
  281. wbcore/serializers/serializers.py +12 -3
  282. wbcore/signals/__init__.py +1 -0
  283. wbcore/signals/clone.py +4 -0
  284. wbcore/signals/models.py +2 -6
  285. wbcore/tasks.py +2 -2
  286. wbcore/templates/wbcore/email_base_template.html +3 -3
  287. wbcore/test/e2e_helpers_methods/e2e_checks.py +10 -4
  288. wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +4 -2
  289. wbcore/test/mixins.py +52 -102
  290. wbcore/test/tests.py +6 -9
  291. wbcore/test/utils.py +3 -4
  292. wbcore/tests/e2e/test_e2e.py +2 -2
  293. wbcore/tests/test_cache/test_decorators.py +4 -7
  294. wbcore/tests/test_configs.py +2 -5
  295. wbcore/tests/test_enums.py +2 -1
  296. wbcore/tests/test_fields/test_choice_fields.py +9 -1
  297. wbcore/tests/test_fields/test_number_fields.py +7 -15
  298. wbcore/tests/test_fields/test_other_fields.py +1 -2
  299. wbcore/tests/test_filters/test_mixins.py +35 -35
  300. wbcore/tests/test_list_display.py +0 -2
  301. wbcore/tests/test_models/test_mixins.py +1 -1
  302. wbcore/tests/test_utils/test_date.py +1 -1
  303. wbcore/tests/test_utils/test_date_builder.py +25 -1
  304. wbcore/tests/test_utils/test_primary.py +1 -1
  305. wbcore/urls.py +4 -0
  306. wbcore/utils/date.py +18 -2
  307. wbcore/utils/figures.py +2 -2
  308. wbcore/utils/models.py +21 -4
  309. wbcore/utils/reportlab.py +7 -0
  310. wbcore/utils/rrules.py +3 -1
  311. wbcore/utils/string_loader.py +1 -1
  312. wbcore/utils/strings.py +3 -3
  313. wbcore/utils/views.py +8 -3
  314. wbcore/viewsets/mixins.py +9 -4
  315. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/METADATA +9 -5
  316. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/RECORD +317 -271
  317. wbcore/contrib/geography/tests/test_serializers.py +0 -7
  318. wbcore/contrib/geography/tests/tests.py +0 -13
  319. wbcore/contrib/io/tests/tests.py +0 -19
  320. wbcore/contrib/workflow/tests/tests.py +0 -25
  321. {wbcore-1.46.0.dist-info → wbcore-1.58.2.dist-info}/WHEEL +0 -0
@@ -123,6 +123,7 @@ class PersonModelDisplay(EntryModelDisplay):
123
123
  grid_template_areas=[
124
124
  ["salutation", "birthday", "formal", "."],
125
125
  ["has_user_account", "last_connection", ".", "."],
126
+ ["description", "description", "description", "description"],
126
127
  ],
127
128
  grid_template_columns=[
128
129
  "minmax(min-content, 1fr)",
@@ -284,14 +285,31 @@ class PersonModelDisplay(EntryModelDisplay):
284
285
  return dp.ListDisplay(
285
286
  fields=[
286
287
  dp.Field(key="name", label=_("Name")),
287
- dp.Field(key="primary_employer_repr", label=_("Primary Employer")),
288
288
  dp.Field(key="customer_status", label=_("Status")),
289
289
  dp.Field(key="position_in_company", label=_("Position")),
290
290
  dp.Field(key="cities", label=_("City")),
291
- dp.Field(key="primary_telephone", label=_("Primary Phone Number")),
292
291
  dp.Field(key="tier", label=_("Tier")),
293
- dp.Field(key="primary_manager_repr", label=_("Primary Relationship Manager")),
294
- dp.Field(key="last_event", label=_("Last Event")),
292
+ dp.Field(
293
+ key=None,
294
+ label=_("Primary Contacts"),
295
+ children=[
296
+ dp.Field(key="primary_employer_repr", label=_("Primary Employer")),
297
+ dp.Field(key="primary_manager_repr", label=_("Relationship Manager")),
298
+ dp.Field(key="primary_telephone", label=_("Telephone")),
299
+ dp.Field(key="primary_email", label=_("Email")),
300
+ dp.Field(key="primary_address", label=_("Address"), show="open"),
301
+ dp.Field(key="primary_website", label=_("Website"), show="open"),
302
+ dp.Field(key="primary_social", label=_("Social"), show="open"),
303
+ ],
304
+ ),
305
+ dp.Field(
306
+ key=None,
307
+ label=_("Last Event"),
308
+ children=[
309
+ dp.Field(key="last_event", label=_("Name")),
310
+ dp.Field(key="last_event_period_endswith", label=_("End Date")),
311
+ ],
312
+ ),
295
313
  dp.Field(key="activity_heat", label=_("Activity Heat")),
296
314
  ],
297
315
  )
@@ -339,19 +357,36 @@ class CompanyModelDisplay(EntryModelDisplay):
339
357
  ),
340
358
  )
341
359
 
342
- display = Display(
360
+ grid_template_areas = [
361
+ ["profile_image", "name", "customer_status", "activity_table"],
362
+ ["profile_image", "primary_telephone", "primary_telephone", "activity_table"],
363
+ ["profile_image", "type", "tier", "activity_table"],
364
+ ["profile_image", "activity_heat", "activity_heat", "activity_table"],
365
+ ["employees_section", "employees_section", "employees_section", "employees_section"],
366
+ ]
367
+ sections = [employees_section]
368
+ if portfolio_fields:
369
+ grid_template_areas.insert(
370
+ 4,
371
+ [
372
+ portfolio_fields.key,
373
+ portfolio_fields.key,
374
+ portfolio_fields.key,
375
+ "activity_table",
376
+ ],
377
+ )
378
+ sections.append(portfolio_fields)
379
+ if aum_table:
380
+ grid_template_areas[-1][-1] = aum_table.key
381
+ sections.append(aum_table)
382
+
383
+ return Display(
343
384
  pages=[
344
385
  Page(
345
386
  title=_("Main Information"),
346
387
  layouts={
347
388
  default(): Layout(
348
- grid_template_areas=[
349
- ["profile_image", "name", "customer_status", "activity_table"],
350
- ["profile_image", "primary_telephone", "primary_telephone", "activity_table"],
351
- ["profile_image", "type", "tier", "activity_table"],
352
- ["profile_image", "activity_heat", "activity_heat", "activity_table"],
353
- ["employees_section", "employees_section", "employees_section", "employees_section"],
354
- ],
389
+ grid_template_areas=grid_template_areas,
355
390
  grid_template_columns=[
356
391
  Style.MIN_CONTENT,
357
392
  "minmax(min-content, 1fr)",
@@ -360,7 +395,7 @@ class CompanyModelDisplay(EntryModelDisplay):
360
395
  ],
361
396
  grid_template_rows=[Style.rem(6), Style.rem(6), Style.rem(6)],
362
397
  grid_auto_rows=Style.MIN_CONTENT,
363
- sections=[employees_section],
398
+ sections=sections,
364
399
  inlines=[
365
400
  Inline(
366
401
  key="activity_table",
@@ -385,29 +420,6 @@ class CompanyModelDisplay(EntryModelDisplay):
385
420
  ]
386
421
  )
387
422
 
388
- # Need to insert the portfolio fields into the display
389
- for page in display.pages:
390
- if page.title == "Main Information":
391
- for layout_key in page.layouts.keys():
392
- if portfolio_fields:
393
- # Insert the section with the AUM fields below the profile picture at the left
394
- page.layouts[layout_key].grid_template_areas.insert(
395
- 4,
396
- [
397
- portfolio_fields.key,
398
- portfolio_fields.key,
399
- portfolio_fields.key,
400
- "activity_table",
401
- ],
402
- )
403
- page.layouts[layout_key].sections.append(portfolio_fields)
404
- if aum_table:
405
- # Insert the section with the AUM table at the bottom right
406
- page.layouts[layout_key].grid_template_areas[-1][-1] = aum_table.key
407
- page.layouts[layout_key].sections.append(aum_table)
408
- break
409
- return display
410
-
411
423
  @classmethod
412
424
  def _get_new_company_instance_display(cls) -> Display:
413
425
  """Returns the display for creating a new company
@@ -460,8 +472,26 @@ class CompanyModelDisplay(EntryModelDisplay):
460
472
  dp.Field(key="type", label=_("Type")),
461
473
  dp.Field(key="tier", label=_("Tier")),
462
474
  dp.Field(key="customer_status", label=_("Status")),
463
- dp.Field(key="primary_manager_repr", label=_("Primary Relationship Manager")),
464
- dp.Field(key="last_event", label=_("Last Event")),
475
+ dp.Field(
476
+ key=None,
477
+ label=_("Primary Contacts"),
478
+ children=[
479
+ dp.Field(key="primary_manager_repr", label=_("Relationship Manager")),
480
+ dp.Field(key="primary_telephone", label=_("Telephone")),
481
+ dp.Field(key="primary_email", label=_("Email")),
482
+ dp.Field(key="primary_address", label=_("Address"), show="open"),
483
+ dp.Field(key="primary_website", label=_("Website"), show="open"),
484
+ dp.Field(key="primary_social", label=_("Social"), show="open"),
485
+ ],
486
+ ),
487
+ dp.Field(
488
+ key=None,
489
+ label=_("Last Event"),
490
+ children=[
491
+ dp.Field(key="last_event", label=_("Name")),
492
+ dp.Field(key="last_event_period_endswith", label=_("Date")),
493
+ ],
494
+ ),
465
495
  dp.Field(key="activity_heat", label=_("Activity Heat")),
466
496
  ]
467
497
  )
@@ -4,7 +4,7 @@ from typing import Optional
4
4
  from django.utils.translation import gettext as _
5
5
 
6
6
  from wbcore.contrib.color.enums import WBColor
7
- from wbcore.contrib.directory.models import ClientManagerRelationship as CMR
7
+ from wbcore.contrib.directory.models import ClientManagerRelationship
8
8
  from wbcore.contrib.icons import WBIcon
9
9
  from wbcore.metadata.configs import display as dp
10
10
  from wbcore.metadata.configs.display.instance_display.shortcuts import (
@@ -15,7 +15,7 @@ from wbcore.metadata.configs.display.instance_display.utils import repeat_field
15
15
  from wbcore.metadata.configs.display.view_config import DisplayViewConfig
16
16
 
17
17
 
18
- class CMR_Color(Enum):
18
+ class ClientManagerRelationshipColor(Enum):
19
19
  DRAFT = WBColor.RED_LIGHT.value
20
20
  PENDING = WBColor.YELLOW_LIGHT.value
21
21
  APPROVED = WBColor.GREEN_LIGHT.value
@@ -93,13 +93,7 @@ class EmployerEmployeeRelationshipDisplayConfig(DisplayViewConfig):
93
93
  condition=("==", True),
94
94
  ),
95
95
  ],
96
- ),
97
- dp.Formatting(
98
- column="primary",
99
- formatting_rules=[
100
- dp.FormattingRule(icon=WBIcon.FAVORITE.icon, condition=("==", True)),
101
- ],
102
- ),
96
+ )
103
97
  ],
104
98
  )
105
99
 
@@ -143,13 +137,7 @@ class EmployeeEmployerDisplayConfig(DisplayViewConfig):
143
137
  condition=("==", True),
144
138
  ),
145
139
  ],
146
- ),
147
- dp.Formatting(
148
- column="primary",
149
- formatting_rules=[
150
- dp.FormattingRule(icon=WBIcon.FAVORITE.icon, condition=("==", True)),
151
- ],
152
- ),
140
+ )
153
141
  ],
154
142
  )
155
143
 
@@ -188,13 +176,7 @@ class EmployerEmployeeDisplayConfig(DisplayViewConfig):
188
176
  condition=("==", True),
189
177
  ),
190
178
  ],
191
- ),
192
- dp.Formatting(
193
- column="primary",
194
- formatting_rules=[
195
- dp.FormattingRule(icon=WBIcon.FAVORITE.icon, condition=("==", True)),
196
- ],
197
- ),
179
+ )
198
180
  ],
199
181
  )
200
182
 
@@ -221,29 +203,29 @@ class ClientManagerModelDisplay(DisplayViewConfig):
221
203
  key="status",
222
204
  items=[
223
205
  dp.LegendItem(
224
- icon=CMR_Color.DRAFT.value,
225
- label=CMR.Status.DRAFT.label,
226
- value=CMR.Status.DRAFT.name,
206
+ icon=ClientManagerRelationshipColor.DRAFT.value,
207
+ label=ClientManagerRelationship.Status.DRAFT.label,
208
+ value=ClientManagerRelationship.Status.DRAFT.name,
227
209
  ),
228
210
  dp.LegendItem(
229
- icon=CMR_Color.PENDING.value,
230
- label=CMR.Status.PENDINGADD.label,
231
- value=CMR.Status.PENDINGADD.name,
211
+ icon=ClientManagerRelationshipColor.PENDING.value,
212
+ label=ClientManagerRelationship.Status.PENDINGADD.label,
213
+ value=ClientManagerRelationship.Status.PENDINGADD.name,
232
214
  ),
233
215
  dp.LegendItem(
234
- icon=CMR_Color.APPROVED.value,
235
- label=CMR.Status.APPROVED.label,
236
- value=CMR.Status.APPROVED.name,
216
+ icon=ClientManagerRelationshipColor.APPROVED.value,
217
+ label=ClientManagerRelationship.Status.APPROVED.label,
218
+ value=ClientManagerRelationship.Status.APPROVED.name,
237
219
  ),
238
220
  dp.LegendItem(
239
- icon=CMR_Color.PENDING_REMOVE.value,
240
- label=CMR.Status.PENDINGREMOVE.label,
241
- value=CMR.Status.PENDINGREMOVE.name,
221
+ icon=ClientManagerRelationshipColor.PENDING_REMOVE.value,
222
+ label=ClientManagerRelationship.Status.PENDINGREMOVE.label,
223
+ value=ClientManagerRelationship.Status.PENDINGREMOVE.name,
242
224
  ),
243
225
  dp.LegendItem(
244
- icon=CMR_Color.REMOVED.value,
245
- label=CMR.Status.REMOVED.label,
246
- value=CMR.Status.REMOVED.name,
226
+ icon=ClientManagerRelationshipColor.REMOVED.value,
227
+ label=ClientManagerRelationship.Status.REMOVED.label,
228
+ value=ClientManagerRelationship.Status.REMOVED.name,
247
229
  ),
248
230
  ],
249
231
  ),
@@ -260,32 +242,26 @@ class ClientManagerModelDisplay(DisplayViewConfig):
260
242
  formatting_rules=[
261
243
  dp.FormattingRule(
262
244
  style={"backgroundColor": WBColor.YELLOW_LIGHT.value},
263
- condition=("==", CMR.Status.PENDINGADD.name),
245
+ condition=("==", ClientManagerRelationship.Status.PENDINGADD.name),
264
246
  ),
265
247
  dp.FormattingRule(
266
248
  style={"backgroundColor": WBColor.YELLOW_DARK.value},
267
- condition=("==", CMR.Status.PENDINGREMOVE.name),
249
+ condition=("==", ClientManagerRelationship.Status.PENDINGREMOVE.name),
268
250
  ),
269
251
  dp.FormattingRule(
270
252
  style={"backgroundColor": WBColor.GREEN_LIGHT.value},
271
- condition=("==", CMR.Status.APPROVED.name),
253
+ condition=("==", ClientManagerRelationship.Status.APPROVED.name),
272
254
  ),
273
255
  dp.FormattingRule(
274
256
  style={"backgroundColor": WBColor.RED_LIGHT.value},
275
- condition=("==", CMR.Status.DRAFT.name),
257
+ condition=("==", ClientManagerRelationship.Status.DRAFT.name),
276
258
  ),
277
259
  dp.FormattingRule(
278
260
  style={"backgroundColor": WBColor.GREY.value},
279
- condition=("==", CMR.Status.REMOVED.name),
261
+ condition=("==", ClientManagerRelationship.Status.REMOVED.name),
280
262
  ),
281
263
  ],
282
- ),
283
- dp.Formatting(
284
- column="primary",
285
- formatting_rules=[
286
- dp.FormattingRule(icon=WBIcon.FAVORITE.icon, condition=("==", True)),
287
- ],
288
- ),
264
+ )
289
265
  ],
290
266
  )
291
267
 
@@ -6,26 +6,7 @@ from wbcore.permissions.shortcuts import is_internal_user
6
6
 
7
7
 
8
8
  class RelationshipEntryModelEndpoint(EndpointViewConfig):
9
- def get_list_endpoint(self, **kwargs):
10
- return reverse(
11
- "wbcore:directory:entry-relationship-detail",
12
- args=[
13
- self.view.kwargs["entry_id"],
14
- self.view.kwargs.get("pk"),
15
- ],
16
- request=self.request,
17
- )
18
-
19
- def get_instance_endpoint(self, **kwargs):
20
- return reverse(
21
- "wbcore:directory:entry-relationship-list",
22
- args=[
23
- self.view.kwargs["entry_id"],
24
- ],
25
- request=self.request,
26
- )
27
-
28
- def get_create_endpoint(self, **kwargs):
9
+ def get_endpoint(self, **kwargs):
29
10
  return reverse(
30
11
  "wbcore:directory:entry-relationship-list",
31
12
  args=[
@@ -57,9 +38,6 @@ class ClientManagerEndpoint(EndpointViewConfig):
57
38
  return None
58
39
  return super().get_delete_endpoint(**kwargs)
59
40
 
60
- def get_list_endpoint(self, **kwargs):
61
- return f"{reverse('wbcore:directory:clientmanagerrelationship-list', args=[], request=self.request)}"
62
-
63
41
 
64
42
  class EmployeeEmployerEndpointConfig(EndpointViewConfig):
65
43
  PK_FIELD = "employee"
@@ -101,6 +79,3 @@ class UserIsClientEndpointConfig(EndpointViewConfig):
101
79
  if is_internal_user(self.request.user, include_superuser=True):
102
80
  return reverse("wbcore:directory:person-list", request=self.request)
103
81
  return None
104
-
105
- def get_list_endpoint(self, **kwargs):
106
- return reverse("wbcore:directory:clientmanagerrelationship-userclient-list", request=self.request)
@@ -10,6 +10,7 @@ from wbcore.contrib.directory.models import (
10
10
  Entry,
11
11
  Person,
12
12
  )
13
+ from wbcore.contrib.i18n.viewsets import ModelTranslateMixin
13
14
 
14
15
  from ...authentication.models import UserActivity
15
16
  from ..filters import CompanyFilter, EntryFilter, PersonFilter, UserIsManagerFilter
@@ -117,7 +118,7 @@ class EntryModelViewSet(EntryPermissionMixin, TransparencyMixin, viewsets.ModelV
117
118
  return self.request.user.profile and self.request.user.profile.is_internal
118
119
 
119
120
 
120
- class PersonModelViewSet(EntryModelViewSet):
121
+ class PersonModelViewSet(ModelTranslateMixin, EntryModelViewSet):
121
122
  LIST_DOCUMENTATION = "directory/markdown/documentation/person.md"
122
123
  button_config_class = PersonModelButtonConfig
123
124
  display_config_class = PersonModelDisplay
@@ -133,6 +134,7 @@ class PersonModelViewSet(EntryModelViewSet):
133
134
  "tier",
134
135
  "primary_manager_repr",
135
136
  "last_event",
137
+ "last_event_period_endswith",
136
138
  "activity_heat",
137
139
  )
138
140
 
@@ -148,11 +150,10 @@ class PersonModelViewSet(EntryModelViewSet):
148
150
  return super().get_serializer_class()
149
151
 
150
152
  def get_queryset(self):
151
- return (
152
- super()
153
- .get_queryset()
154
- .annotate(last_connection=Coalesce(UserActivity.get_latest_login_datetime_subquery(), None))
155
- )
153
+ qs = super().get_queryset()
154
+ if "pk" in self.kwargs:
155
+ qs = qs.annotate(last_connection=Coalesce(UserActivity.get_latest_login_datetime_subquery(), None))
156
+ return qs
156
157
 
157
158
 
158
159
  class CompanyModelViewSet(EntryModelViewSet):
@@ -168,6 +169,7 @@ class CompanyModelViewSet(EntryModelViewSet):
168
169
  "customer_status",
169
170
  "relationship_managers",
170
171
  "last_event",
172
+ "last_event_period_endswith",
171
173
  "activity_heat",
172
174
  )
173
175
  ordering = ["name", "id"]
@@ -1,3 +1,5 @@
1
+ from contextlib import suppress
2
+
1
3
  from django.utils.translation import gettext as _
2
4
 
3
5
  from wbcore.contrib.icons import WBIcon
@@ -17,12 +19,10 @@ class EntryPreviewConfig(PreviewViewConfig):
17
19
  ["primary_telephone"],
18
20
  ["primary_manager_name"],
19
21
  ]
20
- try:
22
+ with suppress(Exception):
21
23
  entry = self.view.get_object()
22
24
  if entry.profile_image:
23
25
  fields.insert(0, "profile_image")
24
- except Exception:
25
- pass
26
26
 
27
27
  return create_simple_display(fields)
28
28
 
@@ -2,8 +2,10 @@ from django.db.models import CharField, F, OuterRef, Q, Subquery, Value
2
2
  from django.db.models.functions import Concat
3
3
  from django.utils.functional import cached_property
4
4
  from dynamic_preferences.registries import global_preferences_registry
5
- from rest_framework import filters
5
+ from rest_framework import filters, status
6
+ from rest_framework.decorators import action
6
7
  from rest_framework.permissions import IsAuthenticated
8
+ from rest_framework.response import Response
7
9
  from rest_fuzzysearch.search import RankedFuzzySearchFilter
8
10
  from reversion.views import RevisionMixin
9
11
 
@@ -19,6 +21,7 @@ from wbcore.filters import DjangoFilterBackend
19
21
  from wbcore.viewsets import ModelViewSet, ReadOnlyModelViewSet, RepresentationViewSet
20
22
 
21
23
  from ..filters import ClientManagerFilter, RelationshipEntryFilter, RelationshipFilter
24
+ from ..permissions import IsClientManagerRelationshipAdmin
22
25
  from ..serializers import (
23
26
  ClientManagerModelSerializer,
24
27
  ClientManagerRelationshipRepresentationSerializer,
@@ -29,7 +32,7 @@ from ..serializers import (
29
32
  RelationshipTypeRepresentationSerializer,
30
33
  UserIsClientModelSerializer,
31
34
  )
32
- from .buttons import EmployerEmployeeRelationshipButtonConfig
35
+ from .buttons import ClientManagerRelationshipButtonConfig, EmployerEmployeeRelationshipButtonConfig
33
36
  from .display import (
34
37
  ClientManagerModelDisplay,
35
38
  EmployeeEmployerDisplayConfig,
@@ -179,6 +182,7 @@ class ClientManagerViewSet(RevisionMixin, ModelViewSet):
179
182
  queryset = ClientManagerRelationship.objects.none()
180
183
  serializer_class = ClientManagerModelSerializer
181
184
  display_config_class = ClientManagerModelDisplay
185
+ button_config_class = ClientManagerRelationshipButtonConfig
182
186
  endpoint_config_class = ClientManagerEndpoint
183
187
  filterset_class = ClientManagerFilter
184
188
  title_config_class = ClientManagerTitleConfig
@@ -197,6 +201,16 @@ class ClientManagerViewSet(RevisionMixin, ModelViewSet):
197
201
  return ClientManagerRelationship.objects.select_related("client", "relationship_manager")
198
202
  return ClientManagerRelationship.objects.none()
199
203
 
204
+ @action(detail=False, methods=["PATCH"], permission_classes=[IsClientManagerRelationshipAdmin])
205
+ def approveallpendingrequests(self, *args, **kwargs):
206
+ for request in ClientManagerRelationship.objects.filter(status=ClientManagerRelationship.Status.PENDINGADD):
207
+ request.approve(by=self.request.user)
208
+ request.save()
209
+ for request in ClientManagerRelationship.objects.filter(status=ClientManagerRelationship.Status.PENDINGREMOVE):
210
+ request.approveremoval(by=self.request.user)
211
+ request.save()
212
+ return Response({}, status=status.HTTP_200_OK)
213
+
200
214
 
201
215
  class UserIsClientViewSet(ReadOnlyModelViewSet):
202
216
  queryset = ClientManagerRelationship.objects.all()
@@ -1,7 +1,6 @@
1
1
  from django.utils.translation import gettext as _
2
2
 
3
- from wbcore.contrib.directory.models import ClientManagerRelationship as CMR
4
- from wbcore.contrib.directory.models import Company, Entry, Person
3
+ from wbcore.contrib.directory.models import ClientManagerRelationship, Company, Entry, Person
5
4
  from wbcore.metadata.configs.titles import TitleViewConfig
6
5
 
7
6
 
@@ -29,7 +28,7 @@ class RelationshipTypeModelTitleConfig(TitleViewConfig):
29
28
  class ClientManagerTitleConfig(TitleViewConfig):
30
29
  def get_instance_title(self):
31
30
  if pk := self.view.kwargs.get("pk"):
32
- cmr_request = CMR.objects.get(id=pk)
31
+ cmr_request = ClientManagerRelationship.objects.get(id=pk)
33
32
  return _("Client Manager Relationship Between {client} & {person}").format(
34
33
  client=str(cmr_request.client), person=str(cmr_request.relationship_manager)
35
34
  )
@@ -13,7 +13,6 @@ from wbcore.contrib.documents.models import (
13
13
  class ShareableLinkFilter(wb_filters.FilterSet):
14
14
  valid = wb_filters.BooleanFilter(label=_("Valid"), method="boolean_is_valid")
15
15
  valid_until = wb_filters.DateTimeRangeFilter(
16
- method=wb_filters.DateRangeFilter.base_date_range_filter_method,
17
16
  label="Valid Until",
18
17
  )
19
18
  link = wb_filters.CharFilter(label=_("Link"), method="filter_uuid")
@@ -36,7 +35,6 @@ class ShareableLinkFilter(wb_filters.FilterSet):
36
35
  class ShareableLinkAccessFilter(wb_filters.FilterSet):
37
36
  clearable = (False,)
38
37
  accessed = wb_filters.DateTimeRangeFilter(
39
- method=wb_filters.DateRangeFilter.base_date_range_filter_method,
40
38
  label="Accessed between date",
41
39
  )
42
40