wbcore 1.54.10__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 (153) hide show
  1. wbcore/cache/decorators.py +3 -3
  2. wbcore/cache/registry.py +3 -2
  3. wbcore/configs/decorators.py +1 -1
  4. wbcore/configurations/configurations/apps.py +2 -2
  5. wbcore/configurations/configurations/authentication.py +1 -1
  6. wbcore/configurations/configurations/base.py +1 -1
  7. wbcore/configurations/configurations/cache.py +1 -1
  8. wbcore/configurations/configurations/maintenance.py +1 -1
  9. wbcore/configurations/configurations/media.py +1 -1
  10. wbcore/configurations/configurations/middleware.py +1 -1
  11. wbcore/configurations/configurations/rest_framework.py +1 -1
  12. wbcore/configurations/configurations/static.py +1 -1
  13. wbcore/configurations/configurations/wbcore.py +1 -1
  14. wbcore/content_type/serializers.py +1 -1
  15. wbcore/content_type/utils.py +3 -3
  16. wbcore/contrib/agenda/viewsets/calendar_items.py +7 -7
  17. wbcore/contrib/ai/llm/config.py +1 -1
  18. wbcore/contrib/authentication/admin.py +2 -2
  19. wbcore/contrib/authentication/filters.py +0 -1
  20. wbcore/contrib/authentication/models/users.py +3 -3
  21. wbcore/contrib/authentication/models/users_activities.py +1 -1
  22. wbcore/contrib/authentication/serializers/users.py +2 -2
  23. wbcore/contrib/authentication/tests/test_tokens.py +3 -3
  24. wbcore/contrib/authentication/tests/test_users.py +0 -1
  25. wbcore/contrib/authentication/viewsets/user_activities.py +2 -1
  26. wbcore/contrib/authentication/viewsets/users.py +6 -4
  27. wbcore/contrib/color/models.py +2 -1
  28. wbcore/contrib/currency/factories.py +1 -1
  29. wbcore/contrib/currency/import_export/backends/fixerio/currency_fx_rates.py +3 -1
  30. wbcore/contrib/currency/models.py +28 -8
  31. wbcore/contrib/currency/serializers.py +5 -1
  32. wbcore/contrib/currency/tests/test_serializers.py +7 -3
  33. wbcore/contrib/currency/tests/test_viewsets.py +1 -1
  34. wbcore/contrib/currency/viewsets/currency.py +2 -2
  35. wbcore/contrib/dataloader/utils.py +2 -2
  36. wbcore/contrib/directory/factories/__init__.py +1 -1
  37. wbcore/contrib/directory/factories/entries.py +1 -1
  38. wbcore/contrib/directory/models/contacts.py +2 -2
  39. wbcore/contrib/directory/models/entries.py +18 -4
  40. wbcore/contrib/directory/models/relationships.py +25 -30
  41. wbcore/contrib/directory/permissions.py +6 -0
  42. wbcore/contrib/directory/serializers/companies.py +15 -8
  43. wbcore/contrib/directory/serializers/contacts.py +8 -8
  44. wbcore/contrib/directory/serializers/entries.py +24 -15
  45. wbcore/contrib/directory/serializers/entry_representations.py +4 -2
  46. wbcore/contrib/directory/serializers/persons.py +8 -9
  47. wbcore/contrib/directory/serializers/relationships.py +2 -2
  48. wbcore/contrib/directory/tests/conftest.py +2 -0
  49. wbcore/contrib/directory/tests/disable_signals.py +11 -1
  50. wbcore/contrib/directory/tests/signals.py +2 -2
  51. wbcore/contrib/directory/tests/test_models.py +88 -66
  52. wbcore/contrib/directory/tests/test_serializers.py +1 -1
  53. wbcore/contrib/directory/tests/test_viewsets.py +8 -8
  54. wbcore/contrib/directory/viewsets/buttons/__init__.py +1 -1
  55. wbcore/contrib/directory/viewsets/buttons/relationships.py +32 -0
  56. wbcore/contrib/directory/viewsets/contacts.py +6 -6
  57. wbcore/contrib/directory/viewsets/display/__init__.py +1 -1
  58. wbcore/contrib/directory/viewsets/display/entries.py +51 -36
  59. wbcore/contrib/directory/viewsets/display/relationships.py +22 -22
  60. wbcore/contrib/directory/viewsets/entries.py +4 -5
  61. wbcore/contrib/directory/viewsets/previews/entries.py +3 -3
  62. wbcore/contrib/directory/viewsets/relationships.py +16 -2
  63. wbcore/contrib/directory/viewsets/titles/relationships.py +2 -3
  64. wbcore/contrib/documents/filters.py +0 -2
  65. wbcore/contrib/example_app/models.py +4 -4
  66. wbcore/contrib/example_app/serializers/person_team.py +4 -4
  67. wbcore/contrib/example_app/tests/e2e/test_teams.py +1 -1
  68. wbcore/contrib/geography/tests/test_viewsets.py +1 -1
  69. wbcore/contrib/guardian/tests/test_model_mixins.py +3 -3
  70. wbcore/contrib/guardian/tests/test_tasks.py +9 -9
  71. wbcore/contrib/guardian/tests/test_viewsets.py +2 -2
  72. wbcore/contrib/icons/backends/default.py +1 -0
  73. wbcore/contrib/icons/backends/material.py +1 -0
  74. wbcore/contrib/icons/icons.py +5 -8
  75. wbcore/contrib/io/exceptions.py +8 -0
  76. wbcore/contrib/io/import_export/backends/stream.py +2 -2
  77. wbcore/contrib/io/imports.py +10 -5
  78. wbcore/contrib/io/models.py +17 -14
  79. wbcore/contrib/io/serializers.py +2 -2
  80. wbcore/contrib/io/tests/test_backends.py +1 -1
  81. wbcore/contrib/io/tests/test_imports.py +1 -1
  82. wbcore/contrib/io/viewset_mixins.py +4 -4
  83. wbcore/contrib/notifications/dispatch.py +18 -7
  84. wbcore/contrib/pandas/filterset.py +8 -7
  85. wbcore/contrib/pandas/views.py +7 -5
  86. wbcore/contrib/tags/models/tags.py +4 -1
  87. wbcore/contrib/workflow/factories/display.py +2 -2
  88. wbcore/contrib/workflow/models/data.py +7 -4
  89. wbcore/contrib/workflow/models/process.py +2 -2
  90. wbcore/contrib/workflow/serializers/data.py +8 -8
  91. wbcore/contrib/workflow/tests/test_models/test_condition.py +1 -1
  92. wbcore/contrib/workflow/workflows/assignees.py +4 -4
  93. wbcore/dynamic_preferences_registry.py +23 -9
  94. wbcore/enums.py +2 -1
  95. wbcore/filters/fields/content_type.py +5 -4
  96. wbcore/filters/fields/datetime.py +34 -9
  97. wbcore/filters/fields/models.py +2 -2
  98. wbcore/filters/filterset.py +22 -6
  99. wbcore/filters/mixins.py +6 -2
  100. wbcore/forms.py +6 -6
  101. wbcore/fsm/markdown_extensions.py +1 -1
  102. wbcore/fsm/mixins.py +7 -4
  103. wbcore/markdown/models.py +8 -5
  104. wbcore/metadata/configs/buttons/bases.py +6 -6
  105. wbcore/metadata/configs/buttons/buttons.py +2 -1
  106. wbcore/metadata/configs/buttons/view_config.py +5 -3
  107. wbcore/metadata/configs/display/display.py +2 -2
  108. wbcore/metadata/configs/display/formatting.py +6 -7
  109. wbcore/metadata/configs/display/list_display.py +6 -7
  110. wbcore/metadata/configs/display/models.py +6 -0
  111. wbcore/metadata/configs/fields.py +6 -1
  112. wbcore/metadata/configs/filter_fields.py +12 -11
  113. wbcore/models/fields.py +2 -2
  114. wbcore/permissions/permissions.py +2 -2
  115. wbcore/permissions/utils.py +2 -2
  116. wbcore/reversion/viewsets/titles.py +4 -3
  117. wbcore/serializers/__init__.py +1 -0
  118. wbcore/serializers/fields/__init__.py +1 -0
  119. wbcore/serializers/fields/datetime.py +35 -6
  120. wbcore/serializers/fields/fields.py +1 -1
  121. wbcore/serializers/fields/fsm.py +1 -1
  122. wbcore/serializers/fields/list.py +1 -1
  123. wbcore/serializers/fields/mixins.py +13 -5
  124. wbcore/serializers/fields/related.py +4 -6
  125. wbcore/serializers/fields/text.py +1 -1
  126. wbcore/serializers/fields/types.py +1 -0
  127. wbcore/serializers/serializers.py +6 -2
  128. wbcore/tasks.py +2 -2
  129. wbcore/templates/wbcore/email_base_template.html +3 -3
  130. wbcore/test/e2e_helpers_methods/e2e_checks.py +10 -4
  131. wbcore/test/e2e_helpers_methods/e2e_helper_methods.py +4 -2
  132. wbcore/test/mixins.py +1 -1
  133. wbcore/test/tests.py +6 -9
  134. wbcore/test/utils.py +3 -4
  135. wbcore/tests/e2e/test_e2e.py +2 -2
  136. wbcore/tests/test_cache/test_decorators.py +3 -3
  137. wbcore/tests/test_configs.py +1 -1
  138. wbcore/tests/test_fields/test_number_fields.py +1 -1
  139. wbcore/tests/test_filters/test_mixins.py +3 -3
  140. wbcore/tests/test_models/test_mixins.py +1 -1
  141. wbcore/tests/test_utils/test_date.py +1 -1
  142. wbcore/tests/test_utils/test_date_builder.py +25 -1
  143. wbcore/utils/date.py +18 -2
  144. wbcore/utils/figures.py +2 -2
  145. wbcore/utils/models.py +3 -2
  146. wbcore/utils/reportlab.py +7 -0
  147. wbcore/utils/rrules.py +1 -1
  148. wbcore/utils/string_loader.py +1 -1
  149. wbcore/utils/strings.py +2 -2
  150. wbcore/viewsets/mixins.py +6 -4
  151. {wbcore-1.54.10.dist-info → wbcore-1.58.2.dist-info}/METADATA +2 -1
  152. {wbcore-1.54.10.dist-info → wbcore-1.58.2.dist-info}/RECORD +153 -151
  153. {wbcore-1.54.10.dist-info → wbcore-1.58.2.dist-info}/WHEEL +0 -0
@@ -22,11 +22,13 @@ from slugify import slugify
22
22
 
23
23
  from wbcore.contrib.agenda.models import CalendarItem
24
24
  from wbcore.contrib.authentication.models import User
25
+ from wbcore.contrib.currency.models import Currency
25
26
  from wbcore.contrib.directory.models.contacts import (
26
27
  AddressContact,
27
28
  BankingContact,
28
29
  ContactLocationChoices,
29
30
  EmailContact,
31
+ SocialMediaContact,
30
32
  TelephoneContact,
31
33
  WebsiteContact,
32
34
  )
@@ -245,10 +247,6 @@ class EntryDefaultQueryset(models.QuerySet):
245
247
  EmailContact.objects.filter(primary=True, entry__id=OuterRef("pk")).values("address")[:1],
246
248
  output_field=CharField(),
247
249
  ),
248
- secondary_email=Subquery(
249
- EmailContact.objects.filter(primary=False, entry__id=OuterRef("pk")).values("address")[:1],
250
- output_field=CharField(),
251
- ),
252
250
  primary_telephone=Subquery(
253
251
  TelephoneContact.objects.filter(primary=True, entry__id=OuterRef("pk")).values("number")[:1],
254
252
  output_field=CharField(),
@@ -263,6 +261,14 @@ class EntryDefaultQueryset(models.QuerySet):
263
261
  .values("primary_address")[:1],
264
262
  output_field=CharField(),
265
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
+ ),
266
272
  last_event=Subquery(
267
273
  CalendarItem.objects.filter(
268
274
  period__endswith__lte=timezone.now(),
@@ -408,6 +414,14 @@ class Entry(ComplexToStringMixin, DeleteToDisableMixin, WBModel):
408
414
  if additional_field_key in self.additional_fields:
409
415
  del self.additional_fields[additional_field_key]
410
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
+
411
425
  @classmethod
412
426
  def get_endpoint_basename(cls):
413
427
  return "wbcore:directory:entry"
@@ -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",
@@ -209,7 +221,7 @@ 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,
@@ -251,7 +263,7 @@ 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,
@@ -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")
@@ -561,6 +555,7 @@ class EmployerEmployeeRelationship(PrimaryMixin):
561
555
  def get_representation_value_key(cls):
562
556
  return "id"
563
557
 
558
+ @classmethod
564
559
  def representation_label_key(cls):
565
560
  return "{{position_name}}"
566
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,7 +127,12 @@ 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",
132
137
  "last_event_period_endswith",
133
138
  "tier",
@@ -138,10 +143,12 @@ class CompanyModelListSerializer(CompanyModelSerializer):
138
143
 
139
144
 
140
145
  class BankModelSerializer(wb_serializers.ModelSerializer):
141
- primary_address = wb_serializers.CharField(allow_null=True, default="", label=_("Primary Address"), read_only=True)
142
- 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)
143
150
  primary_telephone = wb_serializers.TelephoneField(
144
- allow_null=True, default="", label=_("Primary Telephone"), read_only=True
151
+ allow_null=True, label=_("Primary Telephone"), read_only=True, required=False
145
152
  )
146
153
  _relationship_managers = PersonRepresentationSerializer(many=True, source="relationship_managers")
147
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)
@@ -181,7 +181,7 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
181
181
  read_only=True,
182
182
  required=False,
183
183
  )
184
- is_primary_employer = wb_serializers.BooleanField(default=False, read_only=True)
184
+ is_primary_employer = wb_serializers.BooleanField(read_only=True)
185
185
  position_in_company = wb_serializers.CharField(
186
186
  allow_null=True,
187
187
  default="",
@@ -192,19 +192,26 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
192
192
  read_only=True,
193
193
  required=False,
194
194
  )
195
- primary_address = wb_serializers.CharField(default="", label=_("Primary Address"), read_only=True)
196
- primary_email = wb_serializers.CharField(allow_null=True, default="", label=_("Primary Email"), read_only=False)
197
- primary_manager_repr = wb_serializers.CharField(label=_("Primary Manager"), read_only=True)
198
- 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"))
199
211
  profile_image = wb_serializers.ImageField(allow_null=True, required=False, label=_("Profile Image"))
200
212
  _relationship_managers = PersonRepresentationSerializer(many=True, source="relationship_managers")
201
213
  _social_media = SocialMediaContactRepresentationSerializer(many=True, read_only=True, source="social_media")
202
- primary_manager = wb_serializers.PrimaryKeyRelatedField(
203
- queryset=lambda: Person.objects.filter_only_internal(),
204
- default=wb_serializers.CurrentUserDefault("profile"),
205
- label=_("Primary Manager"),
206
- required=False,
207
- )
214
+ primary_manager = wb_serializers.PrimaryKeyRelatedField(label=_("Primary Manager"), required=False, read_only=True)
208
215
  _primary_manager = InternalUserProfileRepresentationSerializer(source="primary_manager")
209
216
 
210
217
  @wb_serializers.register_resource()
@@ -252,8 +259,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
252
259
  if primary_email := data.get("primary_email", None):
253
260
  try:
254
261
  validate_email(primary_email)
255
- except ValidationError:
256
- raise ValidationError({"primary_email": "Invalid e-mail address"})
262
+ except ValidationError as e:
263
+ raise ValidationError({"primary_email": "Invalid e-mail address"}) from e
257
264
 
258
265
  if primary_telephone := data.get("primary_telephone", None):
259
266
  try:
@@ -265,8 +272,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
265
272
  data["primary_telephone"] = formatted_number
266
273
  else:
267
274
  raise ValidationError({"primary_telephone": gettext("Invalid phone number format")})
268
- except Exception:
269
- 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
270
277
  return super().validate(data)
271
278
 
272
279
  def update(self, instance, validated_data):
@@ -333,6 +340,8 @@ class EntryModelSerializer(wb_serializers.ModelSerializer):
333
340
  "primary_email",
334
341
  "primary_manager_repr",
335
342
  "primary_telephone",
343
+ "primary_website",
344
+ "primary_social",
336
345
  "profile_image",
337
346
  "relationship_managers",
338
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:
@@ -15,13 +15,10 @@ from .relationships import PositionRepresentationSerializer
15
15
 
16
16
 
17
17
  class PersonModelSerializer(ModelTranslateSerializerMixin, EntryModelSerializer):
18
- activites_count = wb_serializers.IntegerField(default=0, read_only=True)
19
-
20
18
  primary_employer_repr = wb_serializers.CharField(read_only=True, required=False, label=_("Primary Employer"))
21
19
  has_user_account = wb_serializers.CharField(read_only=True, label=_("User Account"))
22
20
  name = wb_serializers.CharField(read_only=True)
23
21
  last_connection = wb_serializers.DateTimeField(read_only=True, default=None, label=_("Last Connection"))
24
- secondary_email = wb_serializers.CharField(default="", label=_("Secondary Email"), allow_null=True, read_only=True)
25
22
  personality_profile_red = wb_serializers.RangeSelectField(
26
23
  color=WBColor.RED_LIGHT.value, required=False, label=_("Personality Profile Red")
27
24
  )
@@ -32,7 +29,7 @@ class PersonModelSerializer(ModelTranslateSerializerMixin, EntryModelSerializer)
32
29
  color=WBColor.BLUE_LIGHT.value, required=False, label=_("Personality Profile Blue")
33
30
  )
34
31
  _specializations = SpecializationRepresentationSerializer(source="specializations", many=True)
35
- 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)
36
33
 
37
34
  def get_user_account_email(self, obj):
38
35
  if hasattr(obj, "user_account"):
@@ -82,7 +79,6 @@ class PersonModelSerializer(ModelTranslateSerializerMixin, EntryModelSerializer)
82
79
  "name",
83
80
  "computed_str",
84
81
  "active_employee",
85
- "activites_count",
86
82
  "activity_heat",
87
83
  "addresses",
88
84
  "cities",
@@ -104,9 +100,10 @@ class PersonModelSerializer(ModelTranslateSerializerMixin, EntryModelSerializer)
104
100
  "primary_manager_repr",
105
101
  "primary_employer_repr",
106
102
  "primary_telephone",
103
+ "primary_website",
104
+ "primary_social",
107
105
  "profile_image",
108
106
  "salutation",
109
- "secondary_email",
110
107
  "specializations",
111
108
  "_specializations",
112
109
  "tier",
@@ -204,12 +201,14 @@ class PersonModelListSerializer(PersonModelSerializer):
204
201
  "_cities",
205
202
  "customer_status",
206
203
  "primary_employer_repr",
204
+ "primary_address",
205
+ "primary_email",
207
206
  "primary_manager_repr",
207
+ "primary_website",
208
+ "primary_social",
209
+ "primary_telephone",
208
210
  "last_event",
209
211
  "position_in_company",
210
- "primary_email",
211
- "primary_telephone",
212
- "primary_telephone",
213
212
  "tier",
214
213
  "_additional_resources",
215
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")
@@ -2,7 +2,17 @@ import inspect
2
2
  import re
3
3
  from collections import defaultdict
4
4
 
5
- from django.db.models.signals import *
5
+ from django.db.models.signals import (
6
+ ModelSignal,
7
+ post_delete,
8
+ post_init,
9
+ post_migrate,
10
+ post_save,
11
+ pre_delete,
12
+ pre_init,
13
+ pre_migrate,
14
+ pre_save,
15
+ )
6
16
 
7
17
 
8
18
  class DisableSignals(object):
@@ -6,7 +6,7 @@ from wbcore.test.signals import custom_update_kwargs, get_custom_factory
6
6
  from ..factories import (
7
7
  BankFactory,
8
8
  ParentRelationshipTypeFactory,
9
- Random_ClientFactory,
9
+ RandomClientFactory,
10
10
  UserIsManagerEntryFactory,
11
11
  )
12
12
  from ..viewsets import (
@@ -27,7 +27,7 @@ from ..viewsets import (
27
27
 
28
28
  @receiver(get_custom_factory, sender=PersonInChargeRepresentationViewSet)
29
29
  def receive_factory_person_in_charge(sender, *args, **kwargs):
30
- return Random_ClientFactory
30
+ return RandomClientFactory
31
31
 
32
32
 
33
33
  @receiver(get_custom_factory, sender=RelationshipTypeModelViewSet)