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
@@ -0,0 +1,24 @@
1
+ # Generated by Django 5.0.12 on 2025-02-28 10:21
2
+
3
+ import modeltrans.fields
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('directory', '0010_remove_addresscontact_city'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name='person',
16
+ name='description',
17
+ field=models.TextField(blank=True, default=''),
18
+ ),
19
+ migrations.AddField(
20
+ model_name='person',
21
+ name='i18n',
22
+ field=modeltrans.fields.TranslationField(fields=['description'], required_languages=(), virtual_fields=True),
23
+ ),
24
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 5.0.13 on 2025-03-10 14:40
2
+
3
+ import django.db.models.manager
4
+ from django.db import migrations
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('directory', '0011_person_description_person_i18n'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterModelManagers(
15
+ name='person',
16
+ managers=[
17
+ ('all_objects', django.db.models.manager.Manager()),
18
+ ],
19
+ ),
20
+ ]
@@ -0,0 +1,17 @@
1
+ # Generated by Django 5.0.14 on 2025-05-05 09:13
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('directory', '0012_alter_person_managers'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterModelOptions(
14
+ name='clientmanagerrelationship',
15
+ options={'permissions': (('administrate_clientmanagerrelationship', 'Can administrate Client Manager Relationship'),), 'verbose_name': 'Client Manager Relationship', 'verbose_name_plural': 'Client Manager Relationships'},
16
+ ),
17
+ ]
@@ -40,7 +40,7 @@ class BankingContact(PrimaryMixin, WBModel):
40
40
  @classmethod
41
41
  def get_color_map(cls):
42
42
  colors = [WBColor.RED_LIGHT.value, WBColor.YELLOW_LIGHT.value, WBColor.GREEN_LIGHT.value]
43
- return [choice for choice in zip(cls, colors)]
43
+ return [choice for choice in zip(cls, colors, strict=False)]
44
44
 
45
45
  status = FSMField(
46
46
  default=Status.DRAFT,
@@ -227,7 +227,7 @@ class BankingContact(PrimaryMixin, WBModel):
227
227
 
228
228
  notification_types = [
229
229
  create_notification_type(
230
- code="directory.banking+contact.approval",
230
+ code="directory.banking_contact.approval",
231
231
  title="Banking Contact Notification",
232
232
  help_text="Sends out a notification when you want need to approve a change in bank details",
233
233
  )
@@ -17,15 +17,18 @@ from django.utils import timezone
17
17
  from django.utils.functional import cached_property
18
18
  from django.utils.translation import gettext_lazy as _
19
19
  from dynamic_preferences.registries import global_preferences_registry
20
+ from modeltrans.fields import TranslationField
20
21
  from slugify import slugify
21
22
 
22
23
  from wbcore.contrib.agenda.models import CalendarItem
23
24
  from wbcore.contrib.authentication.models import User
25
+ from wbcore.contrib.currency.models import Currency
24
26
  from wbcore.contrib.directory.models.contacts import (
25
27
  AddressContact,
26
28
  BankingContact,
27
29
  ContactLocationChoices,
28
30
  EmailContact,
31
+ SocialMediaContact,
29
32
  TelephoneContact,
30
33
  WebsiteContact,
31
34
  )
@@ -244,10 +247,6 @@ class EntryDefaultQueryset(models.QuerySet):
244
247
  EmailContact.objects.filter(primary=True, entry__id=OuterRef("pk")).values("address")[:1],
245
248
  output_field=CharField(),
246
249
  ),
247
- secondary_email=Subquery(
248
- EmailContact.objects.filter(primary=False, entry__id=OuterRef("pk")).values("address")[:1],
249
- output_field=CharField(),
250
- ),
251
250
  primary_telephone=Subquery(
252
251
  TelephoneContact.objects.filter(primary=True, entry__id=OuterRef("pk")).values("number")[:1],
253
252
  output_field=CharField(),
@@ -262,6 +261,14 @@ class EntryDefaultQueryset(models.QuerySet):
262
261
  .values("primary_address")[:1],
263
262
  output_field=CharField(),
264
263
  ),
264
+ primary_website=Subquery(
265
+ WebsiteContact.objects.filter(primary=True, entry__id=OuterRef("pk")).values("url")[:1],
266
+ output_field=CharField(),
267
+ ),
268
+ primary_social=Subquery(
269
+ SocialMediaContact.objects.filter(primary=True, entry__id=OuterRef("pk")).values("url")[:1],
270
+ output_field=CharField(),
271
+ ),
265
272
  last_event=Subquery(
266
273
  CalendarItem.objects.filter(
267
274
  period__endswith__lte=timezone.now(),
@@ -271,6 +278,15 @@ class EntryDefaultQueryset(models.QuerySet):
271
278
  .order_by("-period__startswith")
272
279
  .values("title")[:1]
273
280
  ),
281
+ last_event_period_endswith=Subquery(
282
+ CalendarItem.objects.filter(
283
+ period__endswith__lte=timezone.now(),
284
+ visibility=CalendarItem.Visibility.PUBLIC,
285
+ entities=OuterRef("pk"),
286
+ )
287
+ .order_by("-period__startswith")
288
+ .values("period__endswith")[:1]
289
+ ),
274
290
  cities=ArrayAgg("addresses__geography_city", filter=Q(addresses__geography_city__isnull=False)),
275
291
  )
276
292
  return qs
@@ -398,6 +414,14 @@ class Entry(ComplexToStringMixin, DeleteToDisableMixin, WBModel):
398
414
  if additional_field_key in self.additional_fields:
399
415
  del self.additional_fields[additional_field_key]
400
416
 
417
+ def get_banking_contact(self, currency: Currency) -> BankingContact | None:
418
+ bank_accounts = self.banking.all()
419
+ if bank_accounts.filter(currency=currency).exists():
420
+ bank_accounts = bank_accounts.filter(currency=currency)
421
+ if bank_accounts.filter(primary=True).exists():
422
+ bank_accounts = bank_accounts.filter(primary=True)
423
+ return bank_accounts.first()
424
+
401
425
  @classmethod
402
426
  def get_endpoint_basename(cls):
403
427
  return "wbcore:directory:entry"
@@ -540,6 +564,8 @@ class Person(Entry):
540
564
  blank=True,
541
565
  verbose_name=_("Specializations"),
542
566
  )
567
+ description = models.TextField(default="", blank=True)
568
+ i18n = TranslationField(fields=["description"])
543
569
 
544
570
  objects = DefaultPersonManager()
545
571
  registered_users = RegisteredPersonManager()
@@ -583,7 +609,7 @@ class Person(Entry):
583
609
  "/".join(self.employers.all().values_list("name", flat=True)) if self.employers.exists() else ""
584
610
  )
585
611
  if employers_repr:
586
- return f'{self.first_name} {self.last_name}{(" (%s)" % employers_repr)}'
612
+ return f"{self.first_name} {self.last_name}{(' (%s)' % employers_repr)}"
587
613
  except ValueError:
588
614
  pass
589
615
  return f"{self.first_name} {self.last_name}"
@@ -148,6 +148,22 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
148
148
  APPROVED = "APPROVED", _("Approved")
149
149
  REMOVED = "REMOVED", _("Removed")
150
150
 
151
+ @property
152
+ def can_update_primary_field(self):
153
+ return super().can_update_primary_field and self.status in [
154
+ ClientManagerRelationship.Status.APPROVED,
155
+ ClientManagerRelationship.Status.PENDINGREMOVE,
156
+ ]
157
+
158
+ def get_related_queryset(self):
159
+ return ClientManagerRelationship.objects.filter(
160
+ client=self.client,
161
+ status__in=[
162
+ ClientManagerRelationship.Status.APPROVED,
163
+ ClientManagerRelationship.Status.PENDINGREMOVE,
164
+ ],
165
+ )
166
+
151
167
  PRIMARY_ATTR_FIELDS = ["client"]
152
168
 
153
169
  relationship_manager = models.ForeignKey(
@@ -156,10 +172,6 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
156
172
  verbose_name=_("Relationship Manager"),
157
173
  related_name="manager_of",
158
174
  )
159
- primary = models.BooleanField(
160
- verbose_name=_("Primary"),
161
- default=False,
162
- )
163
175
  client = models.ForeignKey(
164
176
  on_delete=models.CASCADE,
165
177
  to="directory.Entry",
@@ -194,7 +206,7 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
194
206
  field=status,
195
207
  source=[Status.DRAFT],
196
208
  target=Status.APPROVED,
197
- permission=lambda instance, user: user.has_perm("directory.change_clientmanagerrelationship"),
209
+ permission=lambda instance, user: user.has_perm("directory.administrate_clientmanagerrelationship"),
198
210
  custom={
199
211
  "_transition_button": ActionButton(
200
212
  method=RequestType.PATCH,
@@ -209,13 +221,13 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
209
221
  },
210
222
  )
211
223
  def mngapprove(self, by=None, description=None, **kwargs):
212
- self._handle_primary_status()
224
+ pass
213
225
 
214
226
  @transition(
215
227
  field=status,
216
228
  source=[Status.PENDINGADD],
217
229
  target=Status.DRAFT,
218
- permission=lambda instance, user: user.has_perm("directory.change_clientmanagerrelationship"),
230
+ permission=lambda instance, user: user.has_perm("directory.administrate_clientmanagerrelationship"),
219
231
  custom={
220
232
  "_transition_button": ActionButton(
221
233
  method=RequestType.PATCH,
@@ -236,7 +248,7 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
236
248
  field=status,
237
249
  source=[Status.PENDINGADD],
238
250
  target=Status.APPROVED,
239
- permission=lambda instance, user: user.has_perm("directory.change_clientmanagerrelationship"),
251
+ permission=lambda instance, user: user.has_perm("directory.administrate_clientmanagerrelationship"),
240
252
  custom={
241
253
  "_transition_button": ActionButton(
242
254
  method=RequestType.PATCH,
@@ -251,13 +263,13 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
251
263
  },
252
264
  )
253
265
  def approve(self, by=None, description=None, **kwargs):
254
- self._handle_primary_status()
266
+ pass
255
267
 
256
268
  @transition(
257
269
  field=status,
258
270
  source=[Status.PENDINGREMOVE],
259
271
  target=Status.APPROVED,
260
- permission=lambda instance, user: user.has_perm("directory.change_clientmanagerrelationship"),
272
+ permission=lambda instance, user: user.has_perm("directory.administrate_clientmanagerrelationship"),
261
273
  custom={
262
274
  "_transition_button": ActionButton(
263
275
  method=RequestType.PATCH,
@@ -278,7 +290,7 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
278
290
  field=status,
279
291
  source=[Status.PENDINGREMOVE],
280
292
  target=Status.REMOVED,
281
- permission=lambda instance, user: user.has_perm("directory.change_clientmanagerrelationship"),
293
+ permission=lambda instance, user: user.has_perm("directory.administrate_clientmanagerrelationship"),
282
294
  custom={
283
295
  "_transition_button": ActionButton(
284
296
  method=RequestType.PATCH,
@@ -295,8 +307,8 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
295
307
  def approveremoval(self, by=None, description=None, **kwargs):
296
308
  pass
297
309
 
298
- def is_not_primary(instance):
299
- return not instance.primary
310
+ def is_not_primary(self):
311
+ return not self.primary
300
312
 
301
313
  @transition(
302
314
  field=status,
@@ -319,10 +331,10 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
319
331
  def makeprimary(self, by=None, description=None, **kwargs):
320
332
  self.primary = True
321
333
 
322
- def last_primary(instance):
323
- return not instance.primary and (
324
- ClientManagerRelationship.objects.exclude(id=instance.id)
325
- .filter(status=ClientManagerRelationship.Status.APPROVED, client=instance.client, primary=True)
334
+ def last_primary(self):
335
+ return not self.primary and (
336
+ ClientManagerRelationship.objects.exclude(id=self.id)
337
+ .filter(status=ClientManagerRelationship.Status.APPROVED, client=self.client, primary=True)
326
338
  .exists()
327
339
  )
328
340
 
@@ -373,24 +385,6 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
373
385
  def delete(self, **kwargs):
374
386
  super().delete(no_deletion=False) # For this model we actually want to delete the object
375
387
 
376
- def get_related_queryset(self):
377
- return (
378
- super().get_related_queryset().filter(status=self.Status.APPROVED)
379
- ) # only one approved relationship can be primary
380
-
381
- def _handle_primary_status(self):
382
- if (
383
- self.primary is True
384
- and ClientManagerRelationship.objects.filter(client=self.client, primary=True).exists()
385
- ):
386
- ClientManagerRelationship.objects.filter(client=self.client, primary=True).update(primary=False)
387
-
388
- if (
389
- self.primary is False
390
- and not ClientManagerRelationship.objects.filter(client=self.client, primary=True).exists()
391
- ):
392
- self.primary = True
393
-
394
388
  class Meta:
395
389
  verbose_name = _("Client Manager Relationship")
396
390
  verbose_name_plural = _("Client Manager Relationships")
@@ -402,6 +396,7 @@ class ClientManagerRelationship(ComplexToStringMixin, PrimaryMixin, WBModel):
402
396
  help_text="Sends you a notification when there is a Relationship manager change to approve.",
403
397
  )
404
398
  ]
399
+ permissions = (("administrate_clientmanagerrelationship", "Can administrate Client Manager Relationship"),)
405
400
 
406
401
  @classmethod
407
402
  def get_representation_endpoint(cls):
@@ -560,6 +555,7 @@ class EmployerEmployeeRelationship(PrimaryMixin):
560
555
  def get_representation_value_key(cls):
561
556
  return "id"
562
557
 
558
+ @classmethod
563
559
  def representation_label_key(cls):
564
560
  return "{{position_name}}"
565
561
 
@@ -0,0 +1,6 @@
1
+ from rest_framework.permissions import IsAuthenticated
2
+
3
+
4
+ class IsClientManagerRelationshipAdmin(IsAuthenticated):
5
+ def has_permission(self, request, view):
6
+ return request.user.has_perm("directory.administrate_clientmanagerrelationship")
@@ -26,10 +26,8 @@ class CompanyModelSerializer(EntryModelSerializer):
26
26
  many=True,
27
27
  )
28
28
  _employees = PersonRepresentationSerializer(source="employees", many=True)
29
- is_primary_employer = wb_serializers.BooleanField(default=False, read_only=True)
30
- tier = wb_serializers.CharField(
31
- help_text=settings.DEFAULT_TIERING_HELP_TEXT, required=False, read_only=True, label=_("Tier")
32
- )
29
+ is_primary_employer = wb_serializers.BooleanField(read_only=True)
30
+ tier = wb_serializers.CharField(help_text=settings.DEFAULT_TIERING_HELP_TEXT, label=_("Tier"), required=False)
33
31
  _type = CompanyTypeRepresentationSerializer(source="type")
34
32
 
35
33
  @wb_serializers.register_resource()
@@ -63,6 +61,8 @@ class CompanyModelSerializer(EntryModelSerializer):
63
61
  "primary_email",
64
62
  "primary_manager_repr",
65
63
  "primary_telephone",
64
+ "primary_website",
65
+ "primary_social",
66
66
  "profile_image",
67
67
  "salutation",
68
68
  "signature",
@@ -77,7 +77,7 @@ class CompanyModelSerializer(EntryModelSerializer):
77
77
 
78
78
  class CompanyModelListSerializer(CompanyModelSerializer):
79
79
  eer_id = wb_serializers.CharField(default="", required=False, read_only=True)
80
- is_primary_employer = wb_serializers.BooleanField(default=False, read_only=True)
80
+ is_primary_employer = wb_serializers.BooleanField(read_only=True)
81
81
 
82
82
  @wb_serializers.register_resource()
83
83
  def delete(self, instance, request, user):
@@ -127,8 +127,14 @@ class CompanyModelListSerializer(CompanyModelSerializer):
127
127
  "_customer_status",
128
128
  "eer_id",
129
129
  "is_primary_employer",
130
+ "primary_address",
131
+ "primary_email",
130
132
  "primary_manager_repr",
133
+ "primary_telephone",
134
+ "primary_website",
135
+ "primary_social",
131
136
  "last_event",
137
+ "last_event_period_endswith",
132
138
  "tier",
133
139
  "type",
134
140
  "_type",
@@ -137,10 +143,12 @@ class CompanyModelListSerializer(CompanyModelSerializer):
137
143
 
138
144
 
139
145
  class BankModelSerializer(wb_serializers.ModelSerializer):
140
- primary_address = wb_serializers.CharField(allow_null=True, default="", label=_("Primary Address"), read_only=True)
141
- primary_email = wb_serializers.CharField(allow_null=True, default="", label=_("Primary Email"), read_only=True)
146
+ primary_address = wb_serializers.CharField(
147
+ allow_null=True, label=_("Primary Address"), read_only=True, required=False
148
+ )
149
+ primary_email = wb_serializers.CharField(allow_null=True, label=_("Primary Email"), required=False, read_only=True)
142
150
  primary_telephone = wb_serializers.TelephoneField(
143
- allow_null=True, default="", label=_("Primary Telephone"), read_only=True
151
+ allow_null=True, label=_("Primary Telephone"), read_only=True, required=False
144
152
  )
145
153
  _relationship_managers = PersonRepresentationSerializer(many=True, source="relationship_managers")
146
154
 
@@ -126,8 +126,8 @@ class EmailContactSerializer(wb_serializers.ModelSerializer):
126
126
  if address:
127
127
  try:
128
128
  validate_email(address)
129
- except ValidationError:
130
- raise serializers.ValidationError({"address": _("Invalid e-mail address")})
129
+ except ValidationError as e:
130
+ raise serializers.ValidationError({"address": _("Invalid e-mail address")}) from e
131
131
  return data
132
132
 
133
133
  class Meta:
@@ -261,8 +261,8 @@ class TelephoneContactSerializer(wb_serializers.ModelSerializer):
261
261
  data["number"] = formatted_number
262
262
  else:
263
263
  raise serializers.ValidationError({"number": _("Invalid phone number format")})
264
- except Exception:
265
- raise serializers.ValidationError({"number": _("Invalid phone number format")})
264
+ except Exception as e:
265
+ raise serializers.ValidationError({"number": _("Invalid phone number format")}) from e
266
266
 
267
267
  if entry and formatting_successful:
268
268
  telephone_contact = TelephoneContact.objects.filter(number=formatted_number, entry=entry)
@@ -310,7 +310,7 @@ class BankingContactSerializer(wb_serializers.ModelSerializer):
310
310
  iban_formatted = iban.formatted
311
311
  data["iban"] = iban_formatted
312
312
  except ValueError as e:
313
- raise serializers.ValidationError({"iban": e})
313
+ raise serializers.ValidationError({"iban": e}) from e
314
314
 
315
315
  if entry and iban_formatted:
316
316
  banking_contact = BankingContact.objects.filter(iban=iban_formatted, entry=entry)
@@ -325,7 +325,7 @@ class BankingContactSerializer(wb_serializers.ModelSerializer):
325
325
  try:
326
326
  BIC(swift_bic_repr)
327
327
  except ValueError as e:
328
- raise serializers.ValidationError({"swift_bic": e})
328
+ raise serializers.ValidationError({"swift_bic": e}) from e
329
329
  return data
330
330
 
331
331
  class Meta:
@@ -364,8 +364,8 @@ class SocialMediaContactSerializer(wb_serializers.ModelSerializer):
364
364
  url = data.get("url", self.instance.url if self.instance else "")
365
365
  try:
366
366
  URLValidator()(url)
367
- except ValidationError:
368
- raise serializers.ValidationError({"url": _("The URL you provided seems to be wrong.")})
367
+ except ValidationError as e:
368
+ raise serializers.ValidationError({"url": _("The URL you provided seems to be wrong.")}) from e
369
369
 
370
370
  if self.instance and (
371
371
  ((data_entry := data.get("entry")) != self.instance.entry)
@@ -166,6 +166,7 @@ class SpecializationRepresentationSerializer(wb_serializers.RepresentationSerial
166
166
 
167
167
  class EntryModelSerializer(wb_serializers.ModelSerializer):
168
168
  last_event = wb_serializers.CharField(read_only=True)
169
+ last_event_period_endswith = wb_serializers.DateTimeField(read_only=True)
169
170
  activity_heat = wb_serializers.RangeSelectField(
170
171
  color="rgb(220,20,60)", label=_("Activity Heat"), read_only=True, required=False
171
172
  )
@@ -180,7 +181,7 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
180
181
  read_only=True,
181
182
  required=False,
182
183
  )
183
- is_primary_employer = wb_serializers.BooleanField(default=False, read_only=True)
184
+ is_primary_employer = wb_serializers.BooleanField(read_only=True)
184
185
  position_in_company = wb_serializers.CharField(
185
186
  allow_null=True,
186
187
  default="",
@@ -191,19 +192,26 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
191
192
  read_only=True,
192
193
  required=False,
193
194
  )
194
- primary_address = wb_serializers.CharField(default="", label=_("Primary Address"), read_only=True)
195
- primary_email = wb_serializers.CharField(allow_null=True, default="", label=_("Primary Email"), read_only=False)
196
- primary_manager_repr = wb_serializers.CharField(label=_("Primary Manager"), read_only=True)
197
- primary_telephone = wb_serializers.TelephoneField(allow_null=True, default="", label=_("Primary Telephone"))
195
+ primary_address = wb_serializers.CharField(
196
+ allow_null=True, read_only=True, required=False, label=_("Primary Address")
197
+ )
198
+ primary_email = wb_serializers.CharField(
199
+ allow_null=True, required=False, read_only=False, label=_("Primary Email")
200
+ )
201
+ primary_manager_repr = wb_serializers.CharField(
202
+ allow_null=True, read_only=True, required=False, label=_("Primary Manager")
203
+ )
204
+ primary_telephone = wb_serializers.TelephoneField(
205
+ allow_null=True,
206
+ required=False,
207
+ label=_("Primary Telephone"),
208
+ )
209
+ primary_website = wb_serializers.URLField(allow_null=True, required=False, label=_("Primary Website"))
210
+ primary_social = wb_serializers.URLField(allow_null=True, required=False, label=_("Primary Social"))
198
211
  profile_image = wb_serializers.ImageField(allow_null=True, required=False, label=_("Profile Image"))
199
212
  _relationship_managers = PersonRepresentationSerializer(many=True, source="relationship_managers")
200
213
  _social_media = SocialMediaContactRepresentationSerializer(many=True, read_only=True, source="social_media")
201
- primary_manager = wb_serializers.PrimaryKeyRelatedField(
202
- queryset=lambda: Person.objects.filter_only_internal(),
203
- default=wb_serializers.CurrentUserDefault("profile"),
204
- label=_("Primary Manager"),
205
- required=False,
206
- )
214
+ primary_manager = wb_serializers.PrimaryKeyRelatedField(label=_("Primary Manager"), required=False, read_only=True)
207
215
  _primary_manager = InternalUserProfileRepresentationSerializer(source="primary_manager")
208
216
 
209
217
  @wb_serializers.register_resource()
@@ -251,8 +259,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
251
259
  if primary_email := data.get("primary_email", None):
252
260
  try:
253
261
  validate_email(primary_email)
254
- except ValidationError:
255
- raise ValidationError({"primary_email": "Invalid e-mail address"})
262
+ except ValidationError as e:
263
+ raise ValidationError({"primary_email": "Invalid e-mail address"}) from e
256
264
 
257
265
  if primary_telephone := data.get("primary_telephone", None):
258
266
  try:
@@ -264,8 +272,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
264
272
  data["primary_telephone"] = formatted_number
265
273
  else:
266
274
  raise ValidationError({"primary_telephone": gettext("Invalid phone number format")})
267
- except Exception:
268
- raise ValidationError({"primary_telephone": gettext("Invalid phone number format")})
275
+ except Exception as e:
276
+ raise ValidationError({"primary_telephone": gettext("Invalid phone number format")}) from e
269
277
  return super().validate(data)
270
278
 
271
279
  def update(self, instance, validated_data):
@@ -316,6 +324,7 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
316
324
 
317
325
  fields = (
318
326
  "last_event",
327
+ "last_event_period_endswith",
319
328
  "id",
320
329
  "computed_str",
321
330
  "activity_heat",
@@ -331,6 +340,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
331
340
  "primary_email",
332
341
  "primary_manager_repr",
333
342
  "primary_telephone",
343
+ "primary_website",
344
+ "primary_social",
334
345
  "profile_image",
335
346
  "relationship_managers",
336
347
  "_relationship_managers",
@@ -9,8 +9,10 @@ from ..models import Entry
9
9
  class EntryRepresentationSerializer(wb_serializers.RepresentationSerializer):
10
10
  _detail = wb_serializers.SerializerMethodField()
11
11
  _detail_preview = wb_serializers.HyperlinkField(reverse_name="wbcore:directory:entry-detail")
12
- primary_email = wb_serializers.CharField(default="", label=_("Primary Email"), allow_null=True)
13
- primary_telephone = wb_serializers.TelephoneField(default="", label=_("Primary Telephone"), allow_null=True)
12
+ primary_email = wb_serializers.CharField(read_only=True, required=False, label=_("Primary Email"), allow_null=True)
13
+ primary_telephone = wb_serializers.TelephoneField(
14
+ read_only=True, required=False, label=_("Primary Telephone"), allow_null=True
15
+ )
14
16
 
15
17
  def get__detail(self, obj):
16
18
  if obj.is_company:
@@ -3,6 +3,7 @@ from rest_framework.reverse import reverse
3
3
 
4
4
  from wbcore import serializers as wb_serializers
5
5
  from wbcore.contrib.color.enums import WBColor
6
+ from wbcore.contrib.i18n.serializers.mixins import ModelTranslateSerializerMixin
6
7
 
7
8
  from ..models import Company, EmployerEmployeeRelationship, Person, Position
8
9
  from .entries import (
@@ -13,14 +14,11 @@ from .entries import (
13
14
  from .relationships import PositionRepresentationSerializer
14
15
 
15
16
 
16
- class PersonModelSerializer(EntryModelSerializer):
17
- activites_count = wb_serializers.IntegerField(default=0, read_only=True)
18
-
17
+ class PersonModelSerializer(ModelTranslateSerializerMixin, EntryModelSerializer):
19
18
  primary_employer_repr = wb_serializers.CharField(read_only=True, required=False, label=_("Primary Employer"))
20
19
  has_user_account = wb_serializers.CharField(read_only=True, label=_("User Account"))
21
20
  name = wb_serializers.CharField(read_only=True)
22
21
  last_connection = wb_serializers.DateTimeField(read_only=True, default=None, label=_("Last Connection"))
23
- secondary_email = wb_serializers.CharField(default="", label=_("Secondary Email"), allow_null=True, read_only=True)
24
22
  personality_profile_red = wb_serializers.RangeSelectField(
25
23
  color=WBColor.RED_LIGHT.value, required=False, label=_("Personality Profile Red")
26
24
  )
@@ -31,7 +29,7 @@ class PersonModelSerializer(EntryModelSerializer):
31
29
  color=WBColor.BLUE_LIGHT.value, required=False, label=_("Personality Profile Blue")
32
30
  )
33
31
  _specializations = SpecializationRepresentationSerializer(source="specializations", many=True)
34
- tier = wb_serializers.CharField(read_only=True, help_text=_("Tier of the primary employer"), label=_("Tier"))
32
+ tier = wb_serializers.CharField(help_text=_("Tier of the primary employer"), label=_("Tier"), required=False)
35
33
 
36
34
  def get_user_account_email(self, obj):
37
35
  if hasattr(obj, "user_account"):
@@ -81,7 +79,6 @@ class PersonModelSerializer(EntryModelSerializer):
81
79
  "name",
82
80
  "computed_str",
83
81
  "active_employee",
84
- "activites_count",
85
82
  "activity_heat",
86
83
  "addresses",
87
84
  "cities",
@@ -103,14 +100,17 @@ class PersonModelSerializer(EntryModelSerializer):
103
100
  "primary_manager_repr",
104
101
  "primary_employer_repr",
105
102
  "primary_telephone",
103
+ "primary_website",
104
+ "primary_social",
106
105
  "profile_image",
107
106
  "salutation",
108
- "secondary_email",
109
107
  "specializations",
110
108
  "_specializations",
111
109
  "tier",
112
110
  "_additional_resources",
113
111
  "initials",
112
+ "description",
113
+ "_i18n",
114
114
  )
115
115
 
116
116
 
@@ -201,12 +201,14 @@ class PersonModelListSerializer(PersonModelSerializer):
201
201
  "_cities",
202
202
  "customer_status",
203
203
  "primary_employer_repr",
204
+ "primary_address",
205
+ "primary_email",
204
206
  "primary_manager_repr",
207
+ "primary_website",
208
+ "primary_social",
209
+ "primary_telephone",
205
210
  "last_event",
206
211
  "position_in_company",
207
- "primary_email",
208
- "primary_telephone",
209
- "primary_telephone",
210
212
  "tier",
211
213
  "_additional_resources",
212
214
  )
@@ -131,8 +131,8 @@ class RelationshipModelSerializer(serializers.ModelSerializer):
131
131
  data["from_entry_id"] = self.instance.from_entry.id
132
132
  else:
133
133
  data["from_entry_id"] = self.context["view"].kwargs["entry_id"]
134
- except KeyError:
135
- raise ValidationError(_("From entry has to be set."))
134
+ except KeyError as e:
135
+ raise ValidationError(_("From entry has to be set.")) from e
136
136
  from_entry = data.get("from_entry", getattr(self.instance, "from_entry", None))
137
137
  to_entry = data.get("to_entry", getattr(self.instance, "to_entry", None))
138
138
 
@@ -5,6 +5,7 @@ from django.db.models.signals import pre_migrate
5
5
  from pytest_factoryboy import register
6
6
  from wbcore.contrib.authentication.factories import InternalUserFactory, UserFactory
7
7
  from wbcore.contrib.geography.tests.signals import app_pre_migration
8
+ from wbcore.contrib.currency.factories import CurrencyFactory
8
9
  from wbcore.tests.conftest import *
9
10
 
10
11
  from ..factories import (
@@ -45,6 +46,7 @@ register(RelationshipFactory)
45
46
  register(RelationshipTypeFactory)
46
47
  register(CustomerStatusFactory)
47
48
  register(CompanyTypeFactory)
49
+ register(CurrencyFactory)
48
50
 
49
51
 
50
52
  @pytest.fixture(autouse=True, scope="session")