wbcore 1.59.16__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.
- wbcore/admin.py +0 -1
- wbcore/configurations/configurations/apps.py +1 -0
- wbcore/contrib/agenda/locale/de/LC_MESSAGES/django.po +17 -18
- wbcore/contrib/agenda/locale/en/LC_MESSAGES/django.po +17 -17
- wbcore/contrib/agenda/locale/fr/LC_MESSAGES/django.po +17 -18
- wbcore/contrib/agenda/models/calendar_item.py +9 -0
- wbcore/contrib/authentication/authentication.py +1 -3
- wbcore/contrib/authentication/configurations.py +0 -1
- wbcore/contrib/authentication/locale/de/LC_MESSAGES/django.po +47 -48
- wbcore/contrib/authentication/locale/en/LC_MESSAGES/django.po +47 -47
- wbcore/contrib/authentication/locale/fr/LC_MESSAGES/django.po +47 -48
- wbcore/contrib/authentication/models/users.py +18 -0
- wbcore/contrib/color/admin.py +1 -0
- wbcore/contrib/color/factories.py +1 -0
- wbcore/contrib/content_type/__init__.py +0 -0
- wbcore/contrib/content_type/apps.py +5 -0
- wbcore/{filters/fields/content_type.py → contrib/content_type/filters.py} +18 -0
- wbcore/{content_type → contrib/content_type}/serializers.py +2 -2
- wbcore/contrib/content_type/urls.py +15 -0
- wbcore/contrib/directory/factories/contacts.py +1 -0
- wbcore/contrib/directory/locale/de/LC_MESSAGES/django.po +307 -271
- wbcore/contrib/directory/locale/en/LC_MESSAGES/django.po +305 -268
- wbcore/contrib/directory/locale/fr/LC_MESSAGES/django.po +305 -269
- wbcore/contrib/directory/migrations/0015_alter_emailcontact_address_and_more.py +47 -0
- wbcore/contrib/directory/models/contacts.py +15 -22
- wbcore/contrib/directory/models/entries.py +82 -2
- wbcore/contrib/directory/models/relationships.py +5 -9
- wbcore/contrib/directory/tests/test_models.py +5 -3
- wbcore/contrib/directory/viewsets/endpoints/__init__.py +2 -0
- wbcore/contrib/directory/viewsets/endpoints/entries.py +10 -0
- wbcore/contrib/directory/viewsets/entries.py +9 -2
- wbcore/contrib/documents/locale/de/LC_MESSAGES/django.po +26 -23
- wbcore/contrib/documents/locale/en/LC_MESSAGES/django.po +26 -22
- wbcore/contrib/documents/locale/fr/LC_MESSAGES/django.po +26 -23
- wbcore/contrib/documents/serializers/document_model_relationships.py +1 -1
- wbcore/contrib/documents/viewsets/documents.py +1 -1
- wbcore/contrib/guardian/__init__.py +0 -0
- wbcore/contrib/guardian/filters.py +1 -0
- wbcore/contrib/guardian/models/mixins.py +1 -0
- wbcore/contrib/guardian/tasks.py +1 -0
- wbcore/contrib/guardian/tests/test_model_mixins.py +1 -0
- wbcore/contrib/guardian/tests/test_tasks.py +1 -0
- wbcore/contrib/guardian/tests/test_utils.py +1 -0
- wbcore/contrib/guardian/tests/test_viewsets.py +1 -0
- wbcore/contrib/guardian/urls.py +1 -0
- wbcore/contrib/guardian/utils.py +1 -0
- wbcore/contrib/guardian/viewsets/configs/buttons.py +1 -0
- wbcore/contrib/guardian/viewsets/configs/endpoints.py +1 -0
- wbcore/contrib/guardian/viewsets/viewsets.py +1 -0
- wbcore/contrib/io/locale/de/LC_MESSAGES/django.po +28 -28
- wbcore/contrib/io/locale/en/LC_MESSAGES/django.po +28 -28
- wbcore/contrib/io/locale/fr/LC_MESSAGES/django.po +28 -28
- wbcore/contrib/notifications/dispatch.py +1 -3
- wbcore/contrib/notifications/locale/de/LC_MESSAGES/django.po +2 -3
- wbcore/contrib/notifications/locale/en/LC_MESSAGES/django.po +2 -2
- wbcore/contrib/notifications/locale/fr/LC_MESSAGES/django.po +2 -3
- wbcore/contrib/notifications/models/notification_types.py +5 -7
- wbcore/contrib/notifications/models/notifications.py +2 -2
- wbcore/contrib/notifications/models/tokens.py +2 -2
- wbcore/contrib/tags/filters.py +1 -1
- wbcore/contrib/tags/serializers.py +2 -2
- wbcore/contrib/workflow/locale/de/LC_MESSAGES/django.po +75 -76
- wbcore/contrib/workflow/locale/en/LC_MESSAGES/django.po +75 -75
- wbcore/contrib/workflow/locale/fr/LC_MESSAGES/django.po +75 -76
- wbcore/contrib/workflow/serializers/process.py +4 -4
- wbcore/contrib/workflow/serializers/workflow.py +1 -1
- wbcore/filters/__init__.py +0 -1
- wbcore/filters/fields/__init__.py +0 -1
- wbcore/frontend_user_configuration.py +3 -2
- wbcore/locale/de/LC_MESSAGES/django.po +159 -129
- wbcore/locale/en/LC_MESSAGES/django.po +158 -129
- wbcore/locale/fr/LC_MESSAGES/django.po +159 -129
- wbcore/menus/menus.py +8 -5
- wbcore/metadata/configs/buttons/view_config.py +4 -1
- wbcore/metadata/configs/display/models.py +4 -5
- wbcore/metadata/configs/endpoints.py +7 -5
- wbcore/migrations/0015_delete_genericmodel.py +16 -0
- wbcore/models/base.py +0 -11
- wbcore/release_notes/models.py +2 -4
- wbcore/serializers/fields/fields.py +4 -2
- wbcore/serializers/fields/number.py +9 -0
- wbcore/shares/config.py +2 -2
- wbcore/signals/merge.py +1 -0
- wbcore/test/utils.py +3 -1
- wbcore/tests/test_permissions/test_backend.py +1 -3
- wbcore/tests/test_something.py +2 -2
- wbcore/urls.py +5 -10
- wbcore/utils/models.py +73 -4
- wbcore/utils/views.py +54 -53
- wbcore/viewsets/mixins.py +1 -1
- wbcore/viewsets/viewsets.py +1 -1
- {wbcore-1.59.16.dist-info → wbcore-1.60.0.dist-info}/METADATA +1 -1
- {wbcore-1.59.16.dist-info → wbcore-1.60.0.dist-info}/RECORD +98 -98
- wbcore/content_type/filters.py +0 -20
- wbcore/pandas/__init__.py +0 -53
- wbcore/pandas/fields.py +0 -25
- wbcore/pandas/filterset.py +0 -9
- wbcore/pandas/utils.py +0 -25
- wbcore/pandas/views.py +0 -9
- /wbcore/{content_type → contrib/color}/__init__.py +0 -0
- /wbcore/{content_type → contrib/content_type}/admin.py +0 -0
- /wbcore/{content_type → contrib/content_type}/utils.py +0 -0
- /wbcore/{content_type → contrib/content_type}/viewsets.py +0 -0
- {wbcore-1.59.16.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 =
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
entry
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
|
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
|
-
|
|
429
|
-
.
|
|
430
|
-
|
|
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(
|
|
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
|
|
165
|
+
name += f"{(' (%s)' % company_name)}"
|
|
164
166
|
# Assert
|
|
165
167
|
assert test_person.str_full() == name
|
|
166
168
|
|
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
59
|
+
#: contrib/documents/models/documents.py:64
|
|
61
60
|
msgid "Document Type"
|
|
62
61
|
msgstr "Dokumenttyp"
|
|
63
62
|
|
|
64
|
-
#: contrib/documents/models/documents.py:
|
|
63
|
+
#: contrib/documents/models/documents.py:58
|
|
65
64
|
msgid "Description"
|
|
66
65
|
msgstr "Beschreibung"
|
|
67
66
|
|
|
68
|
-
#: contrib/documents/models/documents.py:
|
|
67
|
+
#: contrib/documents/models/documents.py:59
|
|
69
68
|
msgid "File"
|
|
70
69
|
msgstr "Datei"
|
|
71
70
|
|
|
72
|
-
#: contrib/documents/models/documents.py:
|
|
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:
|
|
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:
|
|
82
|
-
#: contrib/documents/viewsets/display/documents.py:
|
|
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:
|
|
85
|
+
#: contrib/documents/models/documents.py:70
|
|
87
86
|
#: contrib/documents/models/shareable_links.py:16
|
|
88
|
-
#: contrib/documents/viewsets/display/documents.py:
|
|
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:
|
|
92
|
+
#: contrib/documents/models/documents.py:72
|
|
94
93
|
msgid "Created"
|
|
95
94
|
msgstr "Erstellt"
|
|
96
95
|
|
|
97
|
-
#: contrib/documents/models/documents.py:
|
|
98
|
-
#: contrib/documents/viewsets/display/documents.py:
|
|
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:
|
|
101
|
+
#: contrib/documents/models/documents.py:238
|
|
103
102
|
msgid "Document"
|
|
104
103
|
msgstr "Dokument"
|
|
105
104
|
|
|
106
|
-
#: contrib/documents/models/documents.py:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
55
|
+
#: contrib/documents/models/documents.py:64
|
|
56
56
|
msgid "Document Type"
|
|
57
57
|
msgstr ""
|
|
58
58
|
|
|
59
|
-
#: contrib/documents/models/documents.py:
|
|
59
|
+
#: contrib/documents/models/documents.py:58
|
|
60
60
|
msgid "Description"
|
|
61
61
|
msgstr ""
|
|
62
62
|
|
|
63
|
-
#: contrib/documents/models/documents.py:
|
|
63
|
+
#: contrib/documents/models/documents.py:59
|
|
64
64
|
msgid "File"
|
|
65
65
|
msgstr ""
|
|
66
66
|
|
|
67
|
-
#: contrib/documents/models/documents.py:
|
|
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:
|
|
72
|
+
#: contrib/documents/models/documents.py:67
|
|
73
73
|
msgid "System Key"
|
|
74
74
|
msgstr ""
|
|
75
75
|
|
|
76
|
-
#: contrib/documents/models/documents.py:
|
|
77
|
-
#: contrib/documents/viewsets/display/documents.py:
|
|
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:
|
|
81
|
+
#: contrib/documents/models/documents.py:70
|
|
82
82
|
#: contrib/documents/models/shareable_links.py:16
|
|
83
|
-
#: contrib/documents/viewsets/display/documents.py:
|
|
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:
|
|
88
|
+
#: contrib/documents/models/documents.py:72
|
|
89
89
|
msgid "Created"
|
|
90
90
|
msgstr ""
|
|
91
91
|
|
|
92
|
-
#: contrib/documents/models/documents.py:
|
|
93
|
-
#: contrib/documents/viewsets/display/documents.py:
|
|
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:
|
|
97
|
+
#: contrib/documents/models/documents.py:238
|
|
98
98
|
msgid "Document"
|
|
99
99
|
msgstr ""
|
|
100
100
|
|
|
101
|
-
#: contrib/documents/models/documents.py:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|