wbcore 1.59.15__py2.py3-none-any.whl → 1.60.0__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 (105) hide show
  1. wbcore/admin.py +0 -1
  2. wbcore/configurations/configurations/apps.py +1 -0
  3. wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po +17 -18
  4. wbcore/contrib/agenda/locale/en/LC_MESSAGES/django.po +17 -17
  5. wbcore/contrib/agenda/locale/fr/LC_MESSAGES/django.po +17 -18
  6. wbcore/contrib/agenda/models/calendar_item.py +9 -0
  7. wbcore/contrib/authentication/authentication.py +1 -3
  8. wbcore/contrib/authentication/configurations.py +0 -1
  9. wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po +47 -48
  10. wbcore/contrib/authentication/locale/en/LC_MESSAGES/django.po +47 -47
  11. wbcore/contrib/authentication/locale/fr/LC_MESSAGES/django.po +47 -48
  12. wbcore/contrib/authentication/models/users.py +18 -0
  13. wbcore/contrib/color/admin.py +1 -0
  14. wbcore/contrib/color/factories.py +1 -0
  15. wbcore/contrib/content_type/__init__.py +0 -0
  16. wbcore/contrib/content_type/apps.py +5 -0
  17. wbcore/{filters/fields/content_type.py → contrib/content_type/filters.py} +18 -0
  18. wbcore/{content_type → contrib/content_type}/serializers.py +2 -2
  19. wbcore/contrib/content_type/urls.py +15 -0
  20. wbcore/contrib/directory/factories/contacts.py +1 -0
  21. wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po +307 -271
  22. wbcore/contrib/directory/locale/en/LC_MESSAGES/django.po +305 -268
  23. wbcore/contrib/directory/locale/fr/LC_MESSAGES/django.po +305 -269
  24. wbcore/contrib/directory/migrations/0015_alter_emailcontact_address_and_more.py +47 -0
  25. wbcore/contrib/directory/models/contacts.py +15 -22
  26. wbcore/contrib/directory/models/entries.py +82 -2
  27. wbcore/contrib/directory/models/relationships.py +5 -9
  28. wbcore/contrib/directory/tests/test_models.py +5 -3
  29. wbcore/contrib/directory/viewsets/endpoints/__init__.py +2 -0
  30. wbcore/contrib/directory/viewsets/endpoints/entries.py +10 -0
  31. wbcore/contrib/directory/viewsets/entries.py +9 -2
  32. wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po +26 -23
  33. wbcore/contrib/documents/locale/en/LC_MESSAGES/django.po +26 -22
  34. wbcore/contrib/documents/locale/fr/LC_MESSAGES/django.po +26 -23
  35. wbcore/contrib/documents/serializers/document_model_relationships.py +1 -1
  36. wbcore/contrib/documents/viewsets/documents.py +1 -1
  37. wbcore/contrib/guardian/__init__.py +0 -0
  38. wbcore/contrib/guardian/filters.py +1 -0
  39. wbcore/contrib/guardian/models/mixins.py +1 -0
  40. wbcore/contrib/guardian/tasks.py +1 -0
  41. wbcore/contrib/guardian/tests/test_model_mixins.py +1 -0
  42. wbcore/contrib/guardian/tests/test_tasks.py +1 -0
  43. wbcore/contrib/guardian/tests/test_utils.py +1 -0
  44. wbcore/contrib/guardian/tests/test_viewsets.py +1 -0
  45. wbcore/contrib/guardian/urls.py +1 -0
  46. wbcore/contrib/guardian/utils.py +1 -0
  47. wbcore/contrib/guardian/viewsets/configs/buttons.py +1 -0
  48. wbcore/contrib/guardian/viewsets/configs/endpoints.py +1 -0
  49. wbcore/contrib/guardian/viewsets/viewsets.py +1 -0
  50. wbcore/contrib/io/locale/de/LC_MESSAGES/django.po +28 -28
  51. wbcore/contrib/io/locale/en/LC_MESSAGES/django.po +28 -28
  52. wbcore/contrib/io/locale/fr/LC_MESSAGES/django.po +28 -28
  53. wbcore/contrib/notifications/backends/firebase/backends.py +2 -2
  54. wbcore/contrib/notifications/dispatch.py +1 -3
  55. wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po +2 -3
  56. wbcore/contrib/notifications/locale/en/LC_MESSAGES/django.po +2 -2
  57. wbcore/contrib/notifications/locale/fr/LC_MESSAGES/django.po +2 -3
  58. wbcore/contrib/notifications/models/notification_types.py +5 -7
  59. wbcore/contrib/notifications/models/notifications.py +2 -2
  60. wbcore/contrib/notifications/models/tokens.py +2 -2
  61. wbcore/contrib/tags/filters.py +1 -1
  62. wbcore/contrib/tags/serializers.py +2 -2
  63. wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po +75 -76
  64. wbcore/contrib/workflow/locale/en/LC_MESSAGES/django.po +75 -75
  65. wbcore/contrib/workflow/locale/fr/LC_MESSAGES/django.po +75 -76
  66. wbcore/contrib/workflow/serializers/process.py +4 -4
  67. wbcore/contrib/workflow/serializers/workflow.py +1 -1
  68. wbcore/filters/__init__.py +0 -1
  69. wbcore/filters/fields/__init__.py +0 -1
  70. wbcore/frontend_user_configuration.py +3 -2
  71. wbcore/locale/de/LC_MESSAGES/django.po +159 -129
  72. wbcore/locale/en/LC_MESSAGES/django.po +158 -129
  73. wbcore/locale/fr/LC_MESSAGES/django.po +159 -129
  74. wbcore/menus/menus.py +8 -5
  75. wbcore/metadata/configs/buttons/view_config.py +4 -1
  76. wbcore/metadata/configs/display/models.py +4 -5
  77. wbcore/metadata/configs/endpoints.py +7 -5
  78. wbcore/migrations/0015_delete_genericmodel.py +16 -0
  79. wbcore/models/base.py +0 -11
  80. wbcore/release_notes/models.py +2 -4
  81. wbcore/serializers/fields/fields.py +4 -2
  82. wbcore/serializers/fields/number.py +9 -0
  83. wbcore/shares/config.py +2 -2
  84. wbcore/signals/merge.py +1 -0
  85. wbcore/test/utils.py +3 -1
  86. wbcore/tests/test_permissions/test_backend.py +1 -3
  87. wbcore/tests/test_something.py +2 -2
  88. wbcore/urls.py +5 -10
  89. wbcore/utils/models.py +73 -4
  90. wbcore/utils/views.py +54 -53
  91. wbcore/viewsets/mixins.py +1 -1
  92. wbcore/viewsets/viewsets.py +1 -1
  93. {wbcore-1.59.15.dist-info → wbcore-1.60.0.dist-info}/METADATA +1 -1
  94. {wbcore-1.59.15.dist-info → wbcore-1.60.0.dist-info}/RECORD +99 -99
  95. wbcore/content_type/filters.py +0 -20
  96. wbcore/pandas/__init__.py +0 -53
  97. wbcore/pandas/fields.py +0 -25
  98. wbcore/pandas/filterset.py +0 -9
  99. wbcore/pandas/utils.py +0 -25
  100. wbcore/pandas/views.py +0 -9
  101. /wbcore/{content_type → contrib/color}/__init__.py +0 -0
  102. /wbcore/{content_type → contrib/content_type}/admin.py +0 -0
  103. /wbcore/{content_type → contrib/content_type}/utils.py +0 -0
  104. /wbcore/{content_type → contrib/content_type}/viewsets.py +0 -0
  105. {wbcore-1.59.15.dist-info → wbcore-1.60.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,47 @@
1
+ # Generated by Django 5.2.9 on 2026-01-12 12:23
2
+
3
+ from django.db import migrations, models
4
+ from tqdm import tqdm
5
+
6
+
7
+ def cleanup_duplicates(apps, schema_editor):
8
+ EmailContact = apps.get_model("directory", "EmailContact")
9
+ qs = EmailContact.objects.values("address").annotate(c=models.Count("*")).filter(c__gt=1).values("address")
10
+ for rel in tqdm(qs, total=qs.count()):
11
+ a = rel["address"]
12
+
13
+ qs = EmailContact.objects.filter(address=a)
14
+ # delete inactive contacts
15
+ qs.filter(models.Q(entry__isnull=True) | models.Q(entry__is_active=False)).delete()
16
+
17
+ # delete contact linked to company if it is already linked to person
18
+ if qs.filter(entry__entry_type="Person").exists() and qs.filter(entry__entry_type="Company").exists():
19
+ qs.filter(entry__entry_type="Company").delete()
20
+
21
+ # for each duplicates, try to merge it, otherwise delete the email address
22
+ if qs.count() > 1:
23
+ first_contact = qs.earliest("entry_id")
24
+ for tt in qs.exclude(id=first_contact.id):
25
+ tt.delete()
26
+
27
+
28
+ class Migration(migrations.Migration):
29
+
30
+ dependencies = [
31
+ ('directory', '0014_alter_entry_relationship_managers_and_more'),
32
+ ]
33
+
34
+ operations = [
35
+ migrations.RunSQL(sql="SET CONSTRAINTS ALL IMMEDIATE;"),
36
+ migrations.RunPython(cleanup_duplicates),
37
+ migrations.RunSQL(sql="SET CONSTRAINTS ALL DEFERRED;"),
38
+ migrations.AlterField(
39
+ model_name='emailcontact',
40
+ name='address',
41
+ field=models.EmailField(max_length=254, unique=True, verbose_name='Email Address'),
42
+ ),
43
+ migrations.AddConstraint(
44
+ model_name='emailcontact',
45
+ constraint=models.UniqueConstraint(condition=models.Q(('primary', True)), fields=('entry',), name='unique_primary_entry_address'),
46
+ ),
47
+ ]
@@ -1,10 +1,10 @@
1
- from django.contrib.auth import get_user_model
2
1
  from django.db import models
3
- from django.db.models import UniqueConstraint
2
+ from django.db.models import Q, UniqueConstraint
4
3
  from django.utils.translation import gettext_lazy as _
5
4
  from django.utils.translation import pgettext_lazy
6
5
  from django_fsm import FSMField, transition
7
6
 
7
+ from wbcore.contrib.authentication.models.users import User
8
8
  from wbcore.contrib.color.enums import WBColor
9
9
  from wbcore.contrib.icons import WBIcon
10
10
  from wbcore.contrib.notifications.dispatch import send_notification
@@ -203,7 +203,7 @@ class BankingContact(PrimaryMixin, WBModel):
203
203
  msg (str): The Notification message
204
204
  """
205
205
 
206
- user_qs = get_user_model().objects.filter(
206
+ user_qs = User.objects.filter(
207
207
  models.Q(user_permissions__codename__in=["administrate_bankingcontact"])
208
208
  | models.Q(groups__permissions__codename="administrate_bankingcontact")
209
209
  )
@@ -387,7 +387,7 @@ class EmailContact(ComplexToStringMixin, PrimaryMixin, WBModel):
387
387
  "Entry", related_name="emails", on_delete=models.SET_NULL, verbose_name=_("Entry"), null=True, blank=True
388
388
  )
389
389
 
390
- address = models.EmailField(verbose_name=_("Email Address"))
390
+ address = models.EmailField(verbose_name=_("Email Address"), unique=True)
391
391
 
392
392
  def compute_str(self) -> str:
393
393
  repr = self.address
@@ -417,28 +417,21 @@ class EmailContact(ComplexToStringMixin, PrimaryMixin, WBModel):
417
417
  verbose_name_plural = _("E-Mail Contacts")
418
418
  constraints = [
419
419
  models.UniqueConstraint(fields=["entry", "address"], name="entry_address_unique_together"),
420
+ models.UniqueConstraint(fields=["entry"], condition=Q(primary=True), name="unique_primary_entry_address"),
420
421
  ]
421
422
 
422
423
  @classmethod
423
424
  def set_entry_primary_email(cls, entry, address):
424
- primaries = entry.emails.filter(primary=True)
425
- duplicates = entry.emails.filter(address=address.lower())
426
- if primaries.exists():
427
- if duplicates.exists():
428
- entry = entry.emails.get(id=duplicates.first().id)
429
- entry.primary = True
430
- entry.save()
431
- else:
432
- entry = entry.emails.get(id=primaries.first().id)
433
- entry.address = address
434
- entry.save()
435
- else:
436
- if duplicates.exists():
437
- entry = entry.emails.get(id=duplicates.first().id)
438
- entry.primary = True
439
- entry.save()
440
- else:
441
- cls.objects.create(entry=entry, address=address, primary=True)
425
+ try:
426
+ contact = EmailContact.objects.get(address=address, entry=entry)
427
+ except EmailContact.DoesNotExist:
428
+ try:
429
+ contact = EmailContact.objects.get(address=address)
430
+ contact.entry = entry
431
+ except EmailContact.DoesNotExist:
432
+ contact = EmailContact(address=address, entry=entry)
433
+ contact.primary = True
434
+ contact.save()
442
435
 
443
436
 
444
437
  class WebsiteContact(PrimaryMixin, WBModel):
@@ -32,7 +32,11 @@ from wbcore.contrib.directory.models.contacts import (
32
32
  TelephoneContact,
33
33
  WebsiteContact,
34
34
  )
35
- from wbcore.contrib.directory.models.relationships import ClientManagerRelationship, EmployerEmployeeRelationship
35
+ from wbcore.contrib.directory.models.relationships import (
36
+ ClientManagerRelationship,
37
+ EmployerEmployeeRelationship,
38
+ Relationship,
39
+ )
36
40
  from wbcore.contrib.directory.signals import deactivate_profile
37
41
  from wbcore.contrib.directory.typings import Person as PersonDTO
38
42
  from wbcore.models import WBModel
@@ -41,6 +45,8 @@ from wbcore.utils.models import (
41
45
  ActiveObjectManager,
42
46
  ComplexToStringMixin,
43
47
  DeleteToDisableMixin,
48
+ MergeError,
49
+ MergeMixin,
44
50
  )
45
51
 
46
52
 
@@ -306,7 +312,7 @@ class EntryManager(ActiveObjectManager):
306
312
  return self.get_queryset().annotate_all()
307
313
 
308
314
 
309
- class Entry(ComplexToStringMixin, DeleteToDisableMixin, WBModel):
315
+ class Entry(MergeMixin, ComplexToStringMixin, DeleteToDisableMixin, WBModel):
310
316
  AUTOMATICALLY_CLEAN_SOFT_DELETED_OBJECTS = True
311
317
 
312
318
  class EntryType(models.TextChoices):
@@ -434,6 +440,46 @@ class Entry(ComplexToStringMixin, DeleteToDisableMixin, WBModel):
434
440
  def get_representation_value_key(cls):
435
441
  return "id"
436
442
 
443
+ def get_merge_senders_kwargs(self, merged_object):
444
+ if type(self) is not type(merged_object):
445
+ raise MergeError(
446
+ f"Cannot merge {merged_object} (type {type(merged_object)}) into {self} (type {type(self)}): mismatch type"
447
+ )
448
+ if isinstance(self, Person):
449
+ yield (Person, merged_object, self)
450
+ elif isinstance(self, Company):
451
+ yield (Company, merged_object, self)
452
+ yield (Entry, getattr(merged_object, "entry_ptr", merged_object), getattr(self, "entry_ptr", self))
453
+
454
+ def _merge(self, merged_entry, **kwargs):
455
+ models = [
456
+ (BankingContact, "iban"),
457
+ (EmailContact, "address"),
458
+ (SocialMediaContact, "url"),
459
+ (TelephoneContact, "number"),
460
+ (WebsiteContact, "url"),
461
+ ]
462
+ AddressContact.objects.filter(entry=merged_entry).update(entry=self)
463
+ Relationship.objects.filter(to_entry=merged_entry).update(to_entry=self)
464
+ Relationship.objects.filter(from_entry=merged_entry).update(from_entry=self)
465
+
466
+ for model_class, field_name in models:
467
+ for contact in model_class.objects.filter(entry=merged_entry):
468
+ if model_class.objects.filter(**{field_name: getattr(contact, field_name), "entry": self}).exists():
469
+ contact.delete()
470
+ else:
471
+ contact.entry = self
472
+ contact.primary = False
473
+ contact.save()
474
+ for rel in ClientManagerRelationship.objects.filter(client=merged_entry):
475
+ if ClientManagerRelationship.objects.filter(
476
+ client=merged_entry, relationship_manager=rel.relationship_manager
477
+ ).exists():
478
+ rel.delete()
479
+ else:
480
+ rel.client = self
481
+ rel.save()
482
+
437
483
 
438
484
  class PersonDefaultQueryset(EntryDefaultQueryset):
439
485
  def annotate_all(self) -> models.QuerySet:
@@ -595,6 +641,26 @@ class Person(Entry):
595
641
  id=self.id,
596
642
  )
597
643
 
644
+ def _merge(self, merged_person, **kwargs):
645
+ super()._merge(merged_person.entry_ptr, **kwargs)
646
+ for contact in BankingContact.objects.filter(access=merged_person):
647
+ contact.access.remove(merged_person)
648
+ if self not in contact.access.all():
649
+ contact.access.add(self)
650
+ for rel in ClientManagerRelationship.objects.filter(relationship_manager=merged_person):
651
+ if ClientManagerRelationship.objects.filter(relationship_manager=self, client=rel.client).exists():
652
+ rel.delete()
653
+ else:
654
+ rel.relationship_manager = self
655
+ rel.save()
656
+
657
+ for rel in EmployerEmployeeRelationship.objects.filter(employee=merged_person):
658
+ if EmployerEmployeeRelationship.objects.filter(employee=merged_person, employer=rel.employer).exists():
659
+ rel.delete()
660
+ else:
661
+ rel.employer = self
662
+ rel.save()
663
+
598
664
  def str_full(self):
599
665
  """
600
666
  Get the string representation including Employers seperated title
@@ -773,6 +839,20 @@ class Company(Entry):
773
839
  def __str__(self):
774
840
  return f"{self.name}"
775
841
 
842
+ def _merge(self, merged_person, **kwargs):
843
+ super()._merge(merged_person.entry_ptr, **kwargs)
844
+ for p in Person.objects.filter(employers=merged_person):
845
+ p.employers.remove(merged_person)
846
+ if self not in p.employers.all():
847
+ p.employers.add(self)
848
+
849
+ for rel in EmployerEmployeeRelationship.objects.filter(employer=merged_person):
850
+ if EmployerEmployeeRelationship.objects.filter(employer=self, employee=rel.employee).exists():
851
+ rel.delete()
852
+ else:
853
+ rel.employer = self
854
+ rel.save()
855
+
776
856
  def save(self, *args, **kwargs):
777
857
  self.entry_type = "Company"
778
858
  if not self.salutation:
@@ -1,4 +1,3 @@
1
- from django.contrib.auth import get_user_model
2
1
  from django.db import models
3
2
  from django.db.models import Q, UniqueConstraint
4
3
  from django.db.models.signals import post_delete, post_save
@@ -7,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
7
6
  from django_fsm import FSMField, transition
8
7
  from slugify import slugify
9
8
 
9
+ from wbcore.contrib.authentication.models.users import User
10
10
  from wbcore.contrib.icons import WBIcon
11
11
  from wbcore.contrib.notifications.dispatch import send_notification
12
12
  from wbcore.contrib.notifications.utils import create_notification_type
@@ -424,14 +424,10 @@ def post_save_client_manager_relationship(sender, instance, created, **kwargs):
424
424
  ClientManagerRelationship.Status.PENDINGADD.name,
425
425
  ClientManagerRelationship.Status.PENDINGREMOVE.name,
426
426
  ]:
427
- user_qs = (
428
- get_user_model()
429
- .objects.filter(
430
- models.Q(groups__permissions__codename="can_administer_entry_in_charge_request")
431
- | models.Q(user_permissions__codename="can_administer_entry_in_charge_request")
432
- )
433
- .distinct()
434
- )
427
+ user_qs = User.objects.filter(
428
+ models.Q(groups__permissions__codename="can_administer_entry_in_charge_request")
429
+ | models.Q(user_permissions__codename="can_administer_entry_in_charge_request")
430
+ ).distinct()
435
431
  if instance.status == ClientManagerRelationship.Status.PENDINGADD.name:
436
432
  for user in user_qs.all():
437
433
  send_notification(
@@ -70,8 +70,10 @@ class TestSpecificModelContacts:
70
70
  @pytest.mark.parametrize("is_primary, has_duplicates", [(a, b) for a in [True, False] for b in [True, False]])
71
71
  def test_set_entry_primary_email(self, is_primary, has_duplicates, test_entry):
72
72
  # Arrange
73
- email_contact_a = EmailContactFactory(entry=test_entry, primary=is_primary)
74
- email_contact_b = EmailContactFactory(entry=test_entry if has_duplicates else None)
73
+ email_contact_a = EmailContactFactory.create(entry=test_entry, primary=is_primary, address="test1@test.com")
74
+ email_contact_b = EmailContactFactory.create(
75
+ entry=test_entry if has_duplicates else None, address="test2@test.com"
76
+ )
75
77
  # Act
76
78
  EmailContact.set_entry_primary_email(test_entry, email_contact_b.address)
77
79
  self.refresh_from_db([email_contact_a, email_contact_b])
@@ -160,7 +162,7 @@ class TestSpecificModelContacts:
160
162
  if with_employer:
161
163
  company_name = test_company.name
162
164
  test_person.employers.add(test_company)
163
- name += f'{(" (%s)" % company_name)}'
165
+ name += f"{(' (%s)' % company_name)}"
164
166
  # Assert
165
167
  assert test_person.str_full() == name
166
168
 
@@ -12,6 +12,8 @@ from .entries import (
12
12
  EntryModelEndpointConfig,
13
13
  PersonModelEndpointConfig,
14
14
  UserIsManagerEndpointConfig,
15
+ PersonRepresentationEndpointConfig,
16
+ CompanyRepresentationEndpointConfig
15
17
  )
16
18
  from .relationships import (
17
19
  ClientManagerEndpoint,
@@ -29,3 +29,13 @@ class UserIsManagerEndpointConfig(EndpointViewConfig):
29
29
  base_url = "wbcore:directory:clientmanagerrelationship-list"
30
30
  filter_url = f"?relationship_manager={self.request.user.profile.id}"
31
31
  return f"{reverse(base_url, args=[], request=self.request)}{filter_url}"
32
+
33
+
34
+ class PersonRepresentationEndpointConfig(EndpointViewConfig):
35
+ def get_create_endpoint(self, **kwargs):
36
+ return PersonModelEndpointConfig(self.view, self.request, self.instance).get_create_endpoint(**kwargs)
37
+
38
+
39
+ class CompanyRepresentationEndpointConfig(EndpointViewConfig):
40
+ def get_create_endpoint(self, **kwargs):
41
+ return CompanyModelEndpointConfig(self.view, self.request, self.instance).get_create_endpoint(**kwargs)
@@ -11,6 +11,7 @@ from wbcore.contrib.directory.models import (
11
11
  Person,
12
12
  )
13
13
  from wbcore.contrib.i18n.viewsets import ModelTranslateMixin
14
+ from wbcore.utils.views import MergeMixin
14
15
 
15
16
  from ...authentication.models import UserActivity
16
17
  from ..filters import CompanyFilter, EntryFilter, PersonFilter, UserIsManagerFilter
@@ -40,7 +41,9 @@ from .display.entries import (
40
41
  )
41
42
  from .endpoints.entries import (
42
43
  CompanyModelEndpointConfig,
44
+ CompanyRepresentationEndpointConfig,
43
45
  PersonModelEndpointConfig,
46
+ PersonRepresentationEndpointConfig,
44
47
  UserIsManagerEndpointConfig,
45
48
  )
46
49
  from .mixins import EntryPermissionMixin
@@ -66,7 +69,8 @@ class PersonRepresentationViewSet(EntryRepresentationViewSet):
66
69
  queryset = Person.objects.annotate_all()
67
70
  serializer_class = PersonRepresentationSerializer
68
71
  filterset_class = PersonFilter
69
-
72
+ ONLY_READ_ONLY_ENDPOINT = False
73
+ endpoint_config_class = PersonRepresentationEndpointConfig
70
74
  ordering_fields = ("last_name", "first_name")
71
75
  ordering = ("last_name", "first_name", "id")
72
76
 
@@ -79,12 +83,15 @@ class PersonInChargeRepresentationViewSet(PersonRepresentationViewSet):
79
83
  class CompanyRepresentationViewSet(EntryRepresentationViewSet):
80
84
  queryset = Company.objects.annotate_all()
81
85
  serializer_class = CompanyRepresentationSerializer
86
+ ONLY_READ_ONLY_ENDPOINT = False
87
+ endpoint_config_class = CompanyRepresentationEndpointConfig
88
+
82
89
  filterset_class = CompanyFilter
83
90
  ordering_fields = ("name",)
84
91
  ordering = ("name", "id")
85
92
 
86
93
 
87
- class EntryModelViewSet(EntryPermissionMixin, TransparencyMixin, viewsets.ModelViewSet):
94
+ class EntryModelViewSet(MergeMixin, EntryPermissionMixin, TransparencyMixin, viewsets.ModelViewSet):
88
95
  min_rank = 0.25
89
96
  search_fields = ("computed_str", "slugify_computed_str", "emails__address")
90
97
  ordering = ["computed_str", "id"]
@@ -6,12 +6,11 @@
6
6
  # Translators:
7
7
  # Kevin Decoster, 2025
8
8
  #
9
- #, fuzzy
10
9
  msgid ""
11
10
  msgstr ""
12
11
  "Project-Id-Version: PACKAGE VERSION\n"
13
12
  "Report-Msgid-Bugs-To: \n"
14
- "POT-Creation-Date: 2025-05-30 11:37+0200\n"
13
+ "POT-Creation-Date: 2026-01-16 14:04+0100\n"
15
14
  "PO-Revision-Date: 2025-05-30 09:40+0000\n"
16
15
  "Last-Translator: Kevin Decoster, 2025\n"
17
16
  "Language-Team: German (https://app.transifex.com/stainly/teams/171242/de/)\n"
@@ -27,19 +26,19 @@ msgstr ""
27
26
  msgid "Valid"
28
27
  msgstr "Gültig"
29
28
 
30
- #: contrib/documents/filters.py:19
29
+ #: contrib/documents/filters.py:18
31
30
  #: contrib/documents/viewsets/display/shareable_links.py:22
32
31
  msgid "Link"
33
32
  msgstr "Link"
34
33
 
35
- #: contrib/documents/filters.py:50
34
+ #: contrib/documents/filters.py:48
36
35
  #: contrib/documents/models/document_types.py:36
37
36
  #: contrib/documents/viewsets/menu/documents.py:17
38
37
  #: contrib/documents/viewsets/titles/document_types.py:8
39
38
  msgid "Document Types"
40
39
  msgstr "Dokumenttypen"
41
40
 
42
- #: contrib/documents/filters.py:70 contrib/documents/filters.py:76
41
+ #: contrib/documents/filters.py:68 contrib/documents/filters.py:74
43
42
  msgid "Document Count"
44
43
  msgstr "Anzahl an Dokumenten"
45
44
 
@@ -57,63 +56,63 @@ msgid "Parent"
57
56
  msgstr "Elternklasse"
58
57
 
59
58
  #: contrib/documents/models/document_types.py:35
60
- #: contrib/documents/models/documents.py:63
59
+ #: contrib/documents/models/documents.py:64
61
60
  msgid "Document Type"
62
61
  msgstr "Dokumenttyp"
63
62
 
64
- #: contrib/documents/models/documents.py:57
63
+ #: contrib/documents/models/documents.py:58
65
64
  msgid "Description"
66
65
  msgstr "Beschreibung"
67
66
 
68
- #: contrib/documents/models/documents.py:58
67
+ #: contrib/documents/models/documents.py:59
69
68
  msgid "File"
70
69
  msgstr "Datei"
71
70
 
72
- #: contrib/documents/models/documents.py:65
71
+ #: contrib/documents/models/documents.py:66
73
72
  #: contrib/documents/serializers/documents.py:11
74
73
  msgid "System Created"
75
74
  msgstr "Automatisch Erstellt"
76
75
 
77
- #: contrib/documents/models/documents.py:66
76
+ #: contrib/documents/models/documents.py:67
78
77
  msgid "System Key"
79
78
  msgstr "Systemschlüssel"
80
79
 
81
- #: contrib/documents/models/documents.py:68
82
- #: contrib/documents/viewsets/display/documents.py:18
80
+ #: contrib/documents/models/documents.py:69
81
+ #: contrib/documents/viewsets/display/documents.py:19
83
82
  msgid "Valid From"
84
83
  msgstr ""
85
84
 
86
- #: contrib/documents/models/documents.py:69
85
+ #: contrib/documents/models/documents.py:70
87
86
  #: contrib/documents/models/shareable_links.py:16
88
- #: contrib/documents/viewsets/display/documents.py:19
87
+ #: contrib/documents/viewsets/display/documents.py:20
89
88
  #: contrib/documents/viewsets/display/shareable_links.py:20
90
89
  msgid "Valid Until"
91
90
  msgstr "Gültig Bis"
92
91
 
93
- #: contrib/documents/models/documents.py:71
92
+ #: contrib/documents/models/documents.py:72
94
93
  msgid "Created"
95
94
  msgstr "Erstellt"
96
95
 
97
- #: contrib/documents/models/documents.py:72
98
- #: contrib/documents/viewsets/display/documents.py:20
96
+ #: contrib/documents/models/documents.py:73
97
+ #: contrib/documents/viewsets/display/documents.py:21
99
98
  msgid "Updated"
100
99
  msgstr "Verändert"
101
100
 
102
- #: contrib/documents/models/documents.py:237
101
+ #: contrib/documents/models/documents.py:238
103
102
  msgid "Document"
104
103
  msgstr "Dokument"
105
104
 
106
- #: contrib/documents/models/documents.py:238
105
+ #: contrib/documents/models/documents.py:239
107
106
  #: contrib/documents/viewsets/menu/documents.py:6
108
107
  #: contrib/documents/viewsets/titles/documents.py:17
109
108
  msgid "Documents"
110
109
  msgstr "Dokumente"
111
110
 
112
- #: contrib/documents/models/documents.py:270
111
+ #: contrib/documents/models/documents.py:271
113
112
  msgid "A file was shared with you"
114
113
  msgstr "Eine Datei wurde mit Ihnen geteilt"
115
114
 
116
- #: contrib/documents/models/documents.py:275
115
+ #: contrib/documents/models/documents.py:276
117
116
  #, python-brace-format
118
117
  msgid ""
119
118
  "\n"
@@ -124,7 +123,7 @@ msgstr ""
124
123
  " <p>Sie finden Ihre/n {document_type} \"{filename}\" unter diesem <a href={link}>Link</a></p>\n"
125
124
  " "
126
125
 
127
- #: contrib/documents/models/documents.py:281
126
+ #: contrib/documents/models/documents.py:282
128
127
  #, python-brace-format
129
128
  msgid ""
130
129
  "\n"
@@ -222,7 +221,11 @@ msgstr "Name"
222
221
  msgid "Number of Documents"
223
222
  msgstr "Anzahl An Dokumenten"
224
223
 
225
- #: contrib/documents/viewsets/display/documents.py:28
224
+ #: contrib/documents/viewsets/display/documents.py:18
225
+ msgid "Permission"
226
+ msgstr ""
227
+
228
+ #: contrib/documents/viewsets/display/documents.py:29
226
229
  msgid "Created By System"
227
230
  msgstr "Automatisch Erstellt"
228
231
 
@@ -7,7 +7,7 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: PACKAGE VERSION\n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2025-05-30 11:37+0200\n"
10
+ "POT-Creation-Date: 2026-01-16 14:04+0100\n"
11
11
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
12
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -22,19 +22,19 @@ msgstr ""
22
22
  msgid "Valid"
23
23
  msgstr ""
24
24
 
25
- #: contrib/documents/filters.py:19
25
+ #: contrib/documents/filters.py:18
26
26
  #: contrib/documents/viewsets/display/shareable_links.py:22
27
27
  msgid "Link"
28
28
  msgstr ""
29
29
 
30
- #: contrib/documents/filters.py:50
30
+ #: contrib/documents/filters.py:48
31
31
  #: contrib/documents/models/document_types.py:36
32
32
  #: contrib/documents/viewsets/menu/documents.py:17
33
33
  #: contrib/documents/viewsets/titles/document_types.py:8
34
34
  msgid "Document Types"
35
35
  msgstr ""
36
36
 
37
- #: contrib/documents/filters.py:70 contrib/documents/filters.py:76
37
+ #: contrib/documents/filters.py:68 contrib/documents/filters.py:74
38
38
  msgid "Document Count"
39
39
  msgstr ""
40
40
 
@@ -52,63 +52,63 @@ msgid "Parent"
52
52
  msgstr ""
53
53
 
54
54
  #: contrib/documents/models/document_types.py:35
55
- #: contrib/documents/models/documents.py:63
55
+ #: contrib/documents/models/documents.py:64
56
56
  msgid "Document Type"
57
57
  msgstr ""
58
58
 
59
- #: contrib/documents/models/documents.py:57
59
+ #: contrib/documents/models/documents.py:58
60
60
  msgid "Description"
61
61
  msgstr ""
62
62
 
63
- #: contrib/documents/models/documents.py:58
63
+ #: contrib/documents/models/documents.py:59
64
64
  msgid "File"
65
65
  msgstr ""
66
66
 
67
- #: contrib/documents/models/documents.py:65
67
+ #: contrib/documents/models/documents.py:66
68
68
  #: contrib/documents/serializers/documents.py:11
69
69
  msgid "System Created"
70
70
  msgstr ""
71
71
 
72
- #: contrib/documents/models/documents.py:66
72
+ #: contrib/documents/models/documents.py:67
73
73
  msgid "System Key"
74
74
  msgstr ""
75
75
 
76
- #: contrib/documents/models/documents.py:68
77
- #: contrib/documents/viewsets/display/documents.py:18
76
+ #: contrib/documents/models/documents.py:69
77
+ #: contrib/documents/viewsets/display/documents.py:19
78
78
  msgid "Valid From"
79
79
  msgstr ""
80
80
 
81
- #: contrib/documents/models/documents.py:69
81
+ #: contrib/documents/models/documents.py:70
82
82
  #: contrib/documents/models/shareable_links.py:16
83
- #: contrib/documents/viewsets/display/documents.py:19
83
+ #: contrib/documents/viewsets/display/documents.py:20
84
84
  #: contrib/documents/viewsets/display/shareable_links.py:20
85
85
  msgid "Valid Until"
86
86
  msgstr ""
87
87
 
88
- #: contrib/documents/models/documents.py:71
88
+ #: contrib/documents/models/documents.py:72
89
89
  msgid "Created"
90
90
  msgstr ""
91
91
 
92
- #: contrib/documents/models/documents.py:72
93
- #: contrib/documents/viewsets/display/documents.py:20
92
+ #: contrib/documents/models/documents.py:73
93
+ #: contrib/documents/viewsets/display/documents.py:21
94
94
  msgid "Updated"
95
95
  msgstr ""
96
96
 
97
- #: contrib/documents/models/documents.py:237
97
+ #: contrib/documents/models/documents.py:238
98
98
  msgid "Document"
99
99
  msgstr ""
100
100
 
101
- #: contrib/documents/models/documents.py:238
101
+ #: contrib/documents/models/documents.py:239
102
102
  #: contrib/documents/viewsets/menu/documents.py:6
103
103
  #: contrib/documents/viewsets/titles/documents.py:17
104
104
  msgid "Documents"
105
105
  msgstr ""
106
106
 
107
- #: contrib/documents/models/documents.py:270
107
+ #: contrib/documents/models/documents.py:271
108
108
  msgid "A file was shared with you"
109
109
  msgstr ""
110
110
 
111
- #: contrib/documents/models/documents.py:275
111
+ #: contrib/documents/models/documents.py:276
112
112
  #, python-brace-format
113
113
  msgid ""
114
114
  "\n"
@@ -117,7 +117,7 @@ msgid ""
117
117
  " "
118
118
  msgstr ""
119
119
 
120
- #: contrib/documents/models/documents.py:281
120
+ #: contrib/documents/models/documents.py:282
121
121
  #, python-brace-format
122
122
  msgid ""
123
123
  "\n"
@@ -212,7 +212,11 @@ msgstr ""
212
212
  msgid "Number of Documents"
213
213
  msgstr ""
214
214
 
215
- #: contrib/documents/viewsets/display/documents.py:28
215
+ #: contrib/documents/viewsets/display/documents.py:18
216
+ msgid "Permission"
217
+ msgstr ""
218
+
219
+ #: contrib/documents/viewsets/display/documents.py:29
216
220
  msgid "Created By System"
217
221
  msgstr ""
218
222