From 1aa0d58bf7c6d49e38c3fa66fbbb39f4f7ce5a4b Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 5 Oct 2025 09:33:20 +0200 Subject: [PATCH 01/56] init --- tapir/coop/models.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tapir/coop/models.py b/tapir/coop/models.py index 1c3e4224e..79a12b091 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -95,6 +95,7 @@ class Meta: _("Is willing to gift a share"), null=True, blank=True ) create_account_reminder_email_sent = models.BooleanField(default=False) + deleted_at = models.DateTimeField(null=True, blank=True) # Soft-Delete class ShareOwnerQuerySet(models.QuerySet): def with_name(self, search_string: str): @@ -177,6 +178,14 @@ def with_status( objects = ShareOwnerQuerySet.as_manager() + def delete(self, using=None, keep_parents=False): + self.deleted_at = timezone.now() + self.save() + + def restore(self): + self.deleted_at = None + self.save() + def blank_info_fields(self): """Used after a ShareOwner is linked to a user, which is used as the source for user info instead.""" self.first_name = "" From a35819e1648477b786ec224af14a1ba9e4cb4312 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 5 Oct 2025 09:46:03 +0200 Subject: [PATCH 02/56] edit manager --- tapir/coop/models.py | 171 +++++++++++++++++++++++-------------------- 1 file changed, 91 insertions(+), 80 deletions(-) diff --git a/tapir/coop/models.py b/tapir/coop/models.py index 79a12b091..258e11a61 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -31,6 +31,95 @@ from tapir.utils.user_utils import UserUtils +class ShareOwnerQuerySet(models.QuerySet): + def with_name(self, search_string: str): + searches = [s for s in search_string.split(" ") if s != ""] + + combined_filters = Q(last_name__icontains="") + for search in searches: + word_filter = ( + Q(last_name__unaccent__icontains=search) + | Q(first_name__unaccent__icontains=search) + | Q(usage_name__unaccent__icontains=search) + | Q(user__first_name__unaccent__icontains=search) + | Q(user__usage_name__unaccent__icontains=search) + | Q(user__last_name__unaccent__icontains=search) + | Q(company_name__unaccent__icontains=search) + ) + combined_filters = combined_filters & word_filter + + return self.filter(combined_filters) + + def with_status( + self, status: str, at_datetime: datetime.datetime | datetime.date = None + ): + if at_datetime is None: + at_datetime = timezone.now() + + at_datetime = ensure_datetime(at_datetime) + + share_owners_with_nb_of_shares = NumberOfSharesService.annotate_share_owner_queryset_with_nb_of_active_shares( + ShareOwner.objects.all(), at_datetime.date() + ) + members_without_shares = share_owners_with_nb_of_shares.filter( + **{NumberOfSharesService.ANNOTATION_NUMBER_OF_ACTIVE_SHARES: 0} + ) + members_without_shares_ids = list( + members_without_shares.values_list("id", flat=True) + ) + + if status == MemberStatus.SOLD: + return self.filter(id__in=members_without_shares_ids).distinct() + + members_with_valid_shares = self.exclude(id__in=members_without_shares_ids) + + members_with_investing_annotation = InvestingStatusService.annotate_share_owner_queryset_with_investing_status_at_datetime( + ShareOwner.objects.all(), at_datetime + ) + investing_members = members_with_investing_annotation.filter( + **{InvestingStatusService.ANNOTATION_WAS_INVESTING: True} + ) + investing_members_ids = list(investing_members.values_list("id", flat=True)) + + if status == MemberStatus.INVESTING: + return members_with_valid_shares.filter( + id__in=investing_members_ids + ).distinct() + + member_with_shares_and_not_investing: Self = members_with_valid_shares.exclude( + id__in=investing_members_ids + ) + + members_with_paused_annotation = ( + MembershipPauseService.annotate_share_owner_queryset_with_has_active_pause( + ShareOwner.objects.all(), at_datetime.date() + ) + ) + paused_members = members_with_paused_annotation.filter( + **{MembershipPauseService.ANNOTATION_HAS_ACTIVE_PAUSE: True} + ) + paused_members_ids = list(paused_members.values_list("id", flat=True)) + + if status == MemberStatus.PAUSED: + return member_with_shares_and_not_investing.filter( + id__in=paused_members_ids + ).distinct() + + if status == MemberStatus.ACTIVE: + return member_with_shares_and_not_investing.exclude( + id__in=paused_members_ids + ).distinct() + + raise TapirException(f"Invalid status : {status}") + + +class NonDeleted(models.Manager): + def get_queryset(self): + return ShareOwnerQuerySet(self.model, using=self._db).filter( + deleted_at__isnull=True + ) + + class ShareOwner(models.Model): """ShareOwner represents a share_owner of a ShareOwnership. @@ -97,86 +186,8 @@ class Meta: create_account_reminder_email_sent = models.BooleanField(default=False) deleted_at = models.DateTimeField(null=True, blank=True) # Soft-Delete - class ShareOwnerQuerySet(models.QuerySet): - def with_name(self, search_string: str): - searches = [s for s in search_string.split(" ") if s != ""] - - combined_filters = Q(last_name__icontains="") - for search in searches: - word_filter = ( - Q(last_name__unaccent__icontains=search) - | Q(first_name__unaccent__icontains=search) - | Q(usage_name__unaccent__icontains=search) - | Q(user__first_name__unaccent__icontains=search) - | Q(user__usage_name__unaccent__icontains=search) - | Q(user__last_name__unaccent__icontains=search) - | Q(company_name__unaccent__icontains=search) - ) - combined_filters = combined_filters & word_filter - - return self.filter(combined_filters) - - def with_status( - self, status: str, at_datetime: datetime.datetime | datetime.date = None - ): - if at_datetime is None: - at_datetime = timezone.now() - - at_datetime = ensure_datetime(at_datetime) - - share_owners_with_nb_of_shares = NumberOfSharesService.annotate_share_owner_queryset_with_nb_of_active_shares( - ShareOwner.objects.all(), at_datetime.date() - ) - members_without_shares = share_owners_with_nb_of_shares.filter( - **{NumberOfSharesService.ANNOTATION_NUMBER_OF_ACTIVE_SHARES: 0} - ) - members_without_shares_ids = list( - members_without_shares.values_list("id", flat=True) - ) - - if status == MemberStatus.SOLD: - return self.filter(id__in=members_without_shares_ids).distinct() - - members_with_valid_shares = self.exclude(id__in=members_without_shares_ids) - - members_with_investing_annotation = InvestingStatusService.annotate_share_owner_queryset_with_investing_status_at_datetime( - ShareOwner.objects.all(), at_datetime - ) - investing_members = members_with_investing_annotation.filter( - **{InvestingStatusService.ANNOTATION_WAS_INVESTING: True} - ) - investing_members_ids = list(investing_members.values_list("id", flat=True)) - - if status == MemberStatus.INVESTING: - return members_with_valid_shares.filter( - id__in=investing_members_ids - ).distinct() - - member_with_shares_and_not_investing: Self = ( - members_with_valid_shares.exclude(id__in=investing_members_ids) - ) - - members_with_paused_annotation = MembershipPauseService.annotate_share_owner_queryset_with_has_active_pause( - ShareOwner.objects.all(), at_datetime.date() - ) - paused_members = members_with_paused_annotation.filter( - **{MembershipPauseService.ANNOTATION_HAS_ACTIVE_PAUSE: True} - ) - paused_members_ids = list(paused_members.values_list("id", flat=True)) - - if status == MemberStatus.PAUSED: - return member_with_shares_and_not_investing.filter( - id__in=paused_members_ids - ).distinct() - - if status == MemberStatus.ACTIVE: - return member_with_shares_and_not_investing.exclude( - id__in=paused_members_ids - ).distinct() - - raise TapirException(f"Invalid status : {status}") - - objects = ShareOwnerQuerySet.as_manager() + everything = models.Manager() + objects = NonDeleted() def delete(self, using=None, keep_parents=False): self.deleted_at = timezone.now() From 735ef3ef8e588b433be3c73040c782078c323265 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 5 Oct 2025 10:31:44 +0200 Subject: [PATCH 03/56] create migration --- ...areowner_managers_shareowner_deleted_at.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py diff --git a/tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py b/tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py new file mode 100644 index 000000000..cc8239ffe --- /dev/null +++ b/tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.12 on 2025-10-05 08:15 + +import django.db.models.manager +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ( + "coop", + "0055_membershippauseupdatedlogentry_coop_member_old_val_6e6554_gin_and_more", + ), + ] + + operations = [ + migrations.AlterModelManagers( + name="shareowner", + managers=[ + ("everything", django.db.models.manager.Manager()), + ], + ), + migrations.AddField( + model_name="shareowner", + name="deleted_at", + field=models.DateTimeField(blank=True, null=True), + ), + ] From e71615af74468da7de189a2ff88924c4ae809db8 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 5 Oct 2025 10:32:08 +0200 Subject: [PATCH 04/56] fix also ShareOwnerQuerySet outside of Shareowner class --- tapir/coop/views/shareowner.py | 43 ++++++++++++---------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 9423f2b2e..5d4b0914e 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -57,6 +57,7 @@ CreateShareOwnershipsLogEntry, UpdateShareOwnershipLogEntry, ExtraSharesForAccountingRecap, + ShareOwnerQuerySet, ) from tapir.coop.services.investing_status_service import InvestingStatusService from tapir.coop.services.membership_pause_service import MembershipPauseService @@ -662,7 +663,7 @@ def __init__(self, *args, **kwargs): ) @staticmethod - def shift_slot_filter(queryset: ShareOwner.ShareOwnerQuerySet, name, value: str): + def shift_slot_filter(queryset: ShareOwnerQuerySet, name, value: str): return queryset.filter( # Find all Tapir-Users currently enrolled in that shift-name "value" user__in=Shift.objects.filter( @@ -671,7 +672,7 @@ def shift_slot_filter(queryset: ShareOwner.ShareOwnerQuerySet, name, value: str) ).distinct() @staticmethod - def display_name_filter(queryset: ShareOwner.ShareOwnerQuerySet, name, value: str): + def display_name_filter(queryset: ShareOwnerQuerySet, name, value: str): # This is an ugly hack to enable searching by Mitgliedsnummer from the # one-stop search box in the top right if value.isdigit(): @@ -679,13 +680,11 @@ def display_name_filter(queryset: ShareOwner.ShareOwnerQuerySet, name, value: st return queryset.with_name(value).distinct() - def status_filter(self, queryset: ShareOwner.ShareOwnerQuerySet, name, value: str): + def status_filter(self, queryset: ShareOwnerQuerySet, name, value: str): return queryset.with_status(value, self.reference_time).distinct() @staticmethod - def shift_attendance_mode_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: str - ): + def shift_attendance_mode_filter(queryset: ShareOwnerQuerySet, name, value: str): queryset = ShiftAttendanceModeService.annotate_share_owner_queryset_with_attendance_mode_at_datetime( queryset ) @@ -697,7 +696,7 @@ def shift_attendance_mode_filter( @staticmethod def registered_to_abcd_slot_with_capability_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: str + queryset: ShareOwnerQuerySet, name, value: str ): return queryset.filter( user__in=TapirUser.objects.registered_to_abcd_shift_slot_with_capability( @@ -707,44 +706,36 @@ def registered_to_abcd_slot_with_capability_filter( @staticmethod def registered_to_slot_with_capability_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: str + queryset: ShareOwnerQuerySet, name, value: str ): return queryset.filter( user__in=TapirUser.objects.registered_to_shift_slot_with_capability(value) ).distinct() @staticmethod - def has_capability_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: str - ): + def has_capability_filter(queryset: ShareOwnerQuerySet, name, value: str): return queryset.filter( user__in=TapirUser.objects.has_capability(value) ).distinct() @staticmethod - def not_has_capability_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: str - ): + def not_has_capability_filter(queryset: ShareOwnerQuerySet, name, value: str): return queryset.exclude( user__in=TapirUser.objects.has_capability(value) ).distinct() @staticmethod - def has_tapir_account_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: bool - ): + def has_tapir_account_filter(queryset: ShareOwnerQuerySet, name, value: bool): return queryset.exclude(user__isnull=value).distinct() @staticmethod - def abcd_week_filter(queryset: ShareOwner.ShareOwnerQuerySet, name, value: str): + def abcd_week_filter(queryset: ShareOwnerQuerySet, name, value: str): return queryset.filter( user__shift_attendance_templates__slot_template__shift_template__group__name=value ).distinct() @staticmethod - def is_fully_paid_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: bool - ): + def is_fully_paid_filter(queryset: ShareOwnerQuerySet, name, value: bool): payment_filter = { f"{PaymentStatusService.ANNOTATION_CREDITED_PAYMENTS_SUM_AT_DATE}__gte": F( PaymentStatusService.ANNOTATION_EXPECTED_PAYMENTS_SUM_AT_DATE @@ -758,7 +749,7 @@ def is_fully_paid_filter( @staticmethod def is_currently_exempted_from_shifts_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: bool + queryset: ShareOwnerQuerySet, name, value: bool ): exemption_filter = Q( user__shift_user_data__shift_exemptions__in=ShiftExemption.objects.active_temporal() @@ -768,15 +759,11 @@ def is_currently_exempted_from_shifts_filter( return queryset.filter(exemption_filter).distinct() @staticmethod - def has_shift_partner_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: bool - ): + def has_shift_partner_filter(queryset: ShareOwnerQuerySet, name, value: bool): return queryset.filter(user__shift_user_data__shift_partner__isnull=not value) @staticmethod - def is_shift_partner_of_filter( - queryset: ShareOwner.ShareOwnerQuerySet, name, value: bool - ): + def is_shift_partner_of_filter(queryset: ShareOwnerQuerySet, name, value: bool): return queryset.filter( user__shift_user_data__shift_partner_of__isnull=not value ) From 8ee857f118476bbea6a149830e7cd3b159b19265 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 5 Oct 2025 10:32:14 +0200 Subject: [PATCH 05/56] tests --- .../coop/tests/test_shareowner_softdelete.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tapir/coop/tests/test_shareowner_softdelete.py diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py new file mode 100644 index 000000000..8554c48cb --- /dev/null +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -0,0 +1,54 @@ +import pytest +from django.utils import timezone + +from tapir.coop.models import ShareOwner +from tapir.coop.tests.factories import ShareOwnerFactory +from tapir.utils.tests_utils import TapirFactoryTestBase + + +class TestShareOwnershipSoftDelete(TapirFactoryTestBase): + def test_softDelete_DeletedAt_isNotNone(self): + share_owner = ShareOwnerFactory.create() + + assert share_owner.deleted_at is None + + share_owner.delete() + + share_owner.refresh_from_db() + + assert share_owner.deleted_at is not None + assert share_owner.deleted_at <= timezone.now() + + def test_softDelete_restoreDeleted_isNone(self): + share_owner = ShareOwnerFactory.create() + + share_owner.delete() + share_owner.refresh_from_db() + assert share_owner.deleted_at is not None + + share_owner.restore() + share_owner.refresh_from_db() + + assert share_owner.deleted_at is None + + def test_softDelete_nonDeletedManager_containsNotDeletedShareowners(self): + active_owner = ShareOwnerFactory.create() + + soft_deleted_owner = ShareOwnerFactory.create() + soft_deleted_owner.delete() + + non_deleted_owners = ShareOwner.objects.all() + + assert active_owner in non_deleted_owners + assert soft_deleted_owner not in non_deleted_owners + + def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): + owner1 = ShareOwnerFactory.create() + + owner2 = ShareOwnerFactory.create() + owner2.delete() + + all_owners = ShareOwner.everything.all() + + assert owner1 in all_owners + assert owner2 in all_owners From af3d11263b4eb6434deb22fd181cfca38128659b Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 5 Oct 2025 17:42:09 +0200 Subject: [PATCH 06/56] buttons --- .../coop/shareowner_confirm_delete.html | 28 ++++++++++++ .../user_coop_share_ownership_list_tag.html | 5 +++ tapir/coop/urls.py | 5 +++ tapir/coop/views/shareowner.py | 43 +++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 tapir/coop/templates/coop/shareowner_confirm_delete.html diff --git a/tapir/coop/templates/coop/shareowner_confirm_delete.html b/tapir/coop/templates/coop/shareowner_confirm_delete.html new file mode 100644 index 000000000..e0bd2496d --- /dev/null +++ b/tapir/coop/templates/coop/shareowner_confirm_delete.html @@ -0,0 +1,28 @@ +{% extends "core/base.html" %} +{% load utils %} +{% load django_bootstrap5 %} +{% load i18n %} +{% load core %} +{% block title %} + {% translate "Confirm delete" %} {{ object }} +{% endblock title %} +{% block content %} +
+

{{ page_title }}

+
+

Are you sure you want to delete this member?

+

+ Name: {{ card_title|safe }} +

+
+
+ {% csrf_token %} + + {% translate 'Cancel' %} +
+
+{% endblock content %} diff --git a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html index 31f7c7c98..cc77dbef2 100644 --- a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html +++ b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html @@ -21,6 +21,11 @@
edit{% translate 'Edit' %} + + delete{% translate 'Delete' %} + {% endif %} {% endif %} diff --git a/tapir/coop/urls.py b/tapir/coop/urls.py index abfb2689d..22a742931 100644 --- a/tapir/coop/urls.py +++ b/tapir/coop/urls.py @@ -135,6 +135,11 @@ views.ShareOwnerUpdateView.as_view(), name="shareowner_update", ), + path( + "member//delete", + views.ShareOwnerDeleteView.as_view(), + name="shareowner_delete", + ), path( "statistics", views.StatisticsView.as_view(), diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 5d4b0914e..726e71f23 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -7,6 +7,7 @@ from django.contrib import messages from django.contrib.auth.decorators import permission_required, login_required from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin +from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import Q, F from django.http import ( @@ -17,6 +18,7 @@ ) from django.shortcuts import get_object_or_404, redirect from django.template import Template, Context +from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _, pgettext_lazy from django.views import generic, View @@ -278,6 +280,47 @@ def get_context_data(self, **kwargs): return context +class ShareOwnerDeleteView( + LoginRequiredMixin, + PermissionRequiredMixin, + generic.DeleteView, +): + permission_required = PERMISSION_ACCOUNTS_MANAGE + model = ShareOwner + + def get_object(self, queryset=None): + share_owner = super().get_object(queryset) + if self.request.user == share_owner.user: + raise PermissionDenied("You cannot delete your own account.") + return share_owner + + def get_success_url(self): + return reverse("coop:shareowner_list") + + def delete(self, request, *args, **kwargs): + with transaction.atomic(): + self.object = self.get_object() + self.object.delete() + + for callback in on_welcome_session_attendance_update: + callback(self.object) + + return super().delete(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + share_owner = self.object + context["page_title"] = _("Delete member: %(name)s") % { + "name": UserUtils.build_display_name_for_viewer( + share_owner, self.request.user + ) + } + context["card_title"] = _("Are you sure you want to delete: %(name)s?") % { + "name": UserUtils.build_html_link_for_viewer(share_owner, self.request.user) + } + return context + + @require_POST @csrf_protect @login_required From 43e670db490c3113a2d550d754958a2d90784927 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 5 Oct 2025 17:44:02 +0200 Subject: [PATCH 07/56] translations --- .../locale/de/LC_MESSAGES/django.po | 234 ++++++++++-------- 1 file changed, 130 insertions(+), 104 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index d682aca46..8ea4fd907 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-02 10:34+0200\n" +"POT-Creation-Date: 2025-10-05 17:43+0200\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -56,12 +56,12 @@ msgstr "Wichtige E-Mails" msgid "Displayed name" msgstr "Angezeigter Name" -#: accounts/models.py:70 coop/models.py:70 coop/models.py:445 +#: accounts/models.py:70 coop/models.py:159 coop/models.py:465 msgid "Pronouns" msgstr "Pronomen" #: accounts/models.py:71 accounts/templates/accounts/user_detail.html:71 -#: coop/models.py:72 coop/models.py:447 +#: coop/models.py:161 coop/models.py:467 #: coop/templates/coop/draftuser_detail.html:71 #: coop/templates/coop/draftuser_detail.html:142 #: coop/templates/coop/shareowner_detail.html:51 @@ -69,29 +69,29 @@ msgid "Phone number" msgstr "Telefonnummer" #: accounts/models.py:72 accounts/templates/accounts/user_detail.html:81 -#: coop/models.py:73 coop/models.py:448 +#: coop/models.py:162 coop/models.py:468 #: coop/templates/coop/draftuser_detail.html:146 #: coop/templates/coop/shareowner_detail.html:55 msgid "Birthdate" msgstr "Geburtsdatum" -#: accounts/models.py:73 coop/models.py:74 coop/models.py:449 +#: accounts/models.py:73 coop/models.py:163 coop/models.py:469 msgid "Street and house number" msgstr "Straße und Hausnummer" -#: accounts/models.py:74 coop/models.py:75 coop/models.py:450 +#: accounts/models.py:74 coop/models.py:164 coop/models.py:470 msgid "Extra address line" msgstr "Adresszusatz" -#: accounts/models.py:75 coop/models.py:76 coop/models.py:451 +#: accounts/models.py:75 coop/models.py:165 coop/models.py:471 msgid "Postcode" msgstr "Postleitzahl" -#: accounts/models.py:76 coop/models.py:77 coop/models.py:452 +#: accounts/models.py:76 coop/models.py:166 coop/models.py:472 msgid "City" msgstr "Ort" -#: accounts/models.py:77 coop/models.py:78 coop/models.py:453 +#: accounts/models.py:77 coop/models.py:167 coop/models.py:473 msgid "Country" msgstr "Land" @@ -120,7 +120,7 @@ msgid "Allow purchase tracking" msgstr "Erlaube Aufzeichnen deiner Einkäufe" #: accounts/models.py:90 accounts/templates/accounts/user_detail.html:101 -#: coop/models.py:81 coop/models.py:456 +#: coop/models.py:170 coop/models.py:476 #: coop/templates/coop/shareowner_detail.html:75 msgid "Preferred Language" msgstr "Bevorzugte Sprache" @@ -279,8 +279,8 @@ msgid "I agree that my membership card will be scanned at the checkout when I ma msgstr "Ich bin damit einverstanden, dass meine Mitgliedskarte beim Einkauf an der Kasse gescannt und somit mein Einkauf erfasst und gespeichert wird:" #: accounts/templates/accounts/purchase_tracking_card.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:168 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:172 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:173 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:177 msgid "Yes,No" msgstr "Ja, Nein" @@ -299,7 +299,7 @@ msgstr "Aktivieren" msgid "You can only look at your own barcode unless you have admin rights" msgstr "Du kannst nur deinen eigenen Strichcode sehen, es sei denn du hast Admin-Rechte" -#: accounts/templates/accounts/user_detail.html:20 coop/models.py:724 +#: accounts/templates/accounts/user_detail.html:20 coop/models.py:744 #: coop/templates/coop/draftuser_detail.html:86 #: coop/templates/coop/shareowner_detail.html:10 log/views.py:94 #: log/views.py:152 shifts/templates/shifts/shift_day_printable.html:55 @@ -331,8 +331,8 @@ msgstr "Benutzername bearbeiten" #: coop/templates/coop/membershipresignation_detail.html:107 #: coop/templates/coop/shareowner_detail.html:30 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:22 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:95 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:118 #: core/templates/core/featureflag_list.html:30 #: shifts/templates/shifts/shift_detail.html:60 #: shifts/templates/shifts/shift_template_detail.html:25 @@ -523,8 +523,8 @@ msgstr "Schickt mir eine Anleitung!" msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters." msgstr "Der angegebene Name ist ungültig. Er darf nur Buchstaben, Zahlen und die Symbole ./-/_ enthalten." -#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:269 -#: coop/views/shareowner.py:274 +#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:272 +#: coop/views/shareowner.py:277 #, python-format msgid "Edit member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" @@ -641,7 +641,7 @@ msgstr "Anzahl zu erstellender Anteile" msgid "The end date must be later than the start date." msgstr "Das Enddatum muss später als das Start-Datum sein" -#: coop/forms.py:105 coop/models.py:462 +#: coop/forms.py:105 coop/models.py:482 msgid "Number of Shares" msgstr "Anzahl Anteile" @@ -726,172 +726,172 @@ msgstr "Absender und Empfänger der Übertragung der Anteile können nicht ident msgid "Cannot pay out, because shares have been gifted." msgstr "Die Anteile können nicht ausgezahlt werden, da sie verschenkt wurden" -#: coop/models.py:54 +#: coop/models.py:143 msgid "Is company" msgstr "Ist eine Firma" -#: coop/models.py:61 coop/models.py:436 +#: coop/models.py:150 coop/models.py:456 msgid "Administrative first name" msgstr "Amtlicher Vorname" -#: coop/models.py:63 coop/models.py:438 +#: coop/models.py:152 coop/models.py:458 msgid "Last name" msgstr "Nachname" -#: coop/models.py:65 coop/models.py:440 +#: coop/models.py:154 coop/models.py:460 msgid "Usage name" msgstr "Angezeigter Name" -#: coop/models.py:71 coop/models.py:446 +#: coop/models.py:160 coop/models.py:466 msgid "Email address" msgstr "E-Mail-Adresse" -#: coop/models.py:88 +#: coop/models.py:177 msgid "Is investing member" msgstr "Ist investierendes Mitglied" -#: coop/models.py:90 coop/models.py:476 +#: coop/models.py:179 coop/models.py:496 #: coop/templates/coop/draftuser_detail.html:176 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:48 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:167 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:53 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:172 msgid "Ratenzahlung" msgstr "Ratenzahlung" -#: coop/models.py:92 coop/models.py:469 +#: coop/models.py:181 coop/models.py:489 msgid "Attended Welcome Session" msgstr "An Willkommenstreffen teilgenommen" -#: coop/models.py:95 +#: coop/models.py:184 msgid "Is willing to gift a share" msgstr "Ist bereit Anteile zu verschenken" -#: coop/models.py:199 +#: coop/models.py:219 msgid "Cannot be a company and have a Tapir account" msgstr "Kann keine Firma sein und ein Tapir-Konto haben" -#: coop/models.py:215 +#: coop/models.py:235 msgid "User info should be stored in associated Tapir account" msgstr "Benutzer Infos sollen in dem Tapir Konto gespeichert warden" -#: coop/models.py:367 +#: coop/models.py:387 msgid "Not a member" msgstr "Kein Mitlied" -#: coop/models.py:368 coop/templates/coop/draftuser_detail.html:169 +#: coop/models.py:388 coop/templates/coop/draftuser_detail.html:169 msgid "Investing" msgstr "Investierend" -#: coop/models.py:369 coop/templates/coop/draftuser_detail.html:171 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:103 +#: coop/models.py:389 coop/templates/coop/draftuser_detail.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:108 #: coop/views/statistics.py:135 msgid "Active" msgstr "Aktiv" -#: coop/models.py:370 +#: coop/models.py:390 msgid "Paused" msgstr "Pausiert" -#: coop/models.py:465 +#: coop/models.py:485 msgid "Investing member" msgstr "Investierendes Mitglied" -#: coop/models.py:472 +#: coop/models.py:492 msgid "Signed Beteiligungserklärung" msgstr "Beteiligungserklärung unterschrieben" -#: coop/models.py:474 +#: coop/models.py:494 msgid "Paid Entrance Fee" msgstr "Eintrittsgeld bezahlt" -#: coop/models.py:531 +#: coop/models.py:551 msgid "Email address must be set." msgstr "Email-Adresse muss gesetzt sein." -#: coop/models.py:533 +#: coop/models.py:553 msgid "First name must be set." msgstr "Vorname muss gesetzt sein." -#: coop/models.py:535 +#: coop/models.py:555 msgid "Last name must be set." msgstr "Nachname muss gesetzt sein." -#: coop/models.py:539 +#: coop/models.py:559 msgid "Membership agreement must be signed." msgstr "Mitgliedsantrag muss unterschrieben sein." -#: coop/models.py:541 +#: coop/models.py:561 msgid "Amount of requested shares must be positive." msgstr "Die Anzahl der erwünschten Anteile muss positiv sein." -#: coop/models.py:543 +#: coop/models.py:563 msgid "Member already created." msgstr "Mitglied schon vorhanden." -#: coop/models.py:570 +#: coop/models.py:590 msgid "Paying member" msgstr "Zahlendes Mitglied" -#: coop/models.py:578 +#: coop/models.py:598 msgid "Credited member" msgstr "Empfangendes Mitglied" -#: coop/models.py:585 +#: coop/models.py:605 msgid "Amount" msgstr "Betrag" -#: coop/models.py:591 +#: coop/models.py:611 msgid "Payment date" msgstr "Zahldatum" -#: coop/models.py:594 +#: coop/models.py:614 msgid "Creation date" msgstr "Erstellungsdatum" -#: coop/models.py:599 +#: coop/models.py:619 msgid "Created by" msgstr "Erstellt durch" -#: coop/models.py:774 +#: coop/models.py:794 msgid "The cooperative buys the shares back from the member" msgstr "Die Kooperative kauft die Anteile des Mitglieds zurück." -#: coop/models.py:777 +#: coop/models.py:797 #, fuzzy #| msgid "Number of shares to create" msgid "The member gifts the shares to the cooperative" msgstr "Anzahl zu erstellender Anteile" -#: coop/models.py:779 +#: coop/models.py:799 msgid "The shares get transferred to another member" msgstr "Die Anteile werden an ein anderes Mitglied übertragen" -#: coop/models.py:782 +#: coop/models.py:802 msgid "Financial reasons" msgstr "Finanzielle Gründe" -#: coop/models.py:783 +#: coop/models.py:803 msgid "Health reasons" msgstr "Gesundheit" -#: coop/models.py:784 +#: coop/models.py:804 msgid "Distance" msgstr "Entfernung" -#: coop/models.py:785 +#: coop/models.py:805 msgid "Strategic orientation of SuperCoop" msgstr "Strategische Ausrichtung von Supercoop" -#: coop/models.py:786 +#: coop/models.py:806 msgid "Other" msgstr "Andere" -#: coop/models.py:791 +#: coop/models.py:811 #, fuzzy #| msgid "Edit shareowner" msgid "Shareowner" msgstr "Mitglied bearbeiten" -#: coop/models.py:810 +#: coop/models.py:830 msgid "Leave this empty if the resignation type is not a transfer to another member" msgstr "Lass das Feld leer, wenn es sich nicht um eine Übertragung auf ein anderes Mitglied handelt" @@ -923,6 +923,7 @@ msgstr "Aktive Mitglieder mit Tapir-Konto" #: coop/templates/coop/confirm_delete_share_ownership.html:6 #: coop/templates/coop/confirm_delete_share_ownership.html:12 #: coop/templates/coop/draftuser_detail.html:20 +#: coop/templates/coop/shareowner_confirm_delete.html:7 #: financingcampaign/templates/financingcampaign/confirm_delete.html:6 #: financingcampaign/templates/financingcampaign/confirm_delete.html:12 msgid "Confirm delete" @@ -930,7 +931,9 @@ msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 #: coop/templates/coop/confirm_delete_share_ownership.html:24 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:127 +#: coop/templates/coop/shareowner_confirm_delete.html:22 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:27 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:132 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 #: shifts/templates/shifts/shift_confirm_delete.html:30 msgid "Delete" @@ -996,9 +999,9 @@ msgid "List of similar members" msgstr "Liste alle E-Mails" #: coop/templates/coop/draftuser_detail.html:69 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:31 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:88 -#: coop/views/shareowner.py:595 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:36 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:93 +#: coop/views/shareowner.py:639 #: shifts/templates/shifts/user_shifts_overview_tag.html:21 msgid "Status" msgstr "Status" @@ -1021,7 +1024,7 @@ msgid "Shares requested" msgstr "Angeforderte Anteile" #: coop/templates/coop/draftuser_detail.html:190 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:53 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 #: shifts/models.py:44 msgid "Welcome Session" msgstr "Willkommenstreffen" @@ -1163,8 +1166,19 @@ msgstr "" "

\n" " " -#: coop/templates/coop/email/co_purchaser_updated.body.html:24 -#, python-format +#: coop/templates/coop/email/co_purchaser_updated.body.html:20 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "

\n" +#| " If this is an error and does not correspond to your request, please send a short\n" +#| " email to the Member Office to let us know.\n" +#| "

\n" +#| "

\n" +#| " Cooperative greetings,
\n" +#| " The Member Office\n" +#| "

\n" +#| " " msgid "" "\n" "

\n" @@ -1172,7 +1186,7 @@ msgid "" " email to the Member Office to let us know.\n" "

\n" "

\n" -" Cooperative greetings,
\n" +" Cooperative greetings,
\n" " The Member Office\n" "

\n" " " @@ -1964,7 +1978,7 @@ msgid "General Tapir Accounts" msgstr "Tapir-Konto erstellen" #: coop/templates/coop/incoming_payment_list.html:9 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:160 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:165 msgid "Payments" msgstr "Zahlungen" @@ -2268,6 +2282,11 @@ msgstr "" msgid "Organisation logo" msgstr "" +#: coop/templates/coop/shareowner_confirm_delete.html:25 +#: shifts/templates/shifts/user_shifts_overview_tag.html:172 +msgid "Cancel" +msgstr "Abmelden" + #: coop/templates/coop/shareowner_detail.html:22 msgid "Go to user page" msgstr "Zur Nutzer*innen-Seite" @@ -2354,7 +2373,7 @@ msgstr "" msgid "Membership confirmation" msgstr "Mitgliedsbestätigung" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:35 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 #, python-format msgid "" "\n" @@ -2362,7 +2381,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:45 #, python-format msgid "" "\n" @@ -2370,43 +2389,43 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:61 #: shifts/models.py:955 shifts/templates/shifts/shift_day_printable.html:56 #: shifts/templates/shifts/shift_detail.html:297 #: shifts/templates/shifts/shift_detail_printable.html:51 msgid "Attended" msgstr "Teilgenommen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:63 #: shifts/models.py:954 msgid "Pending" msgstr "Ausstehend" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:70 msgid "Mark Attended" msgstr "Als teilgenommen markieren" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:79 msgid "Owned shares" msgstr "Anteile" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:82 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:87 msgid "List of shares owned by this member" msgstr "Liste der Anteile die dieses Mitglied gehören" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:86 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:91 msgid "Starts at" msgstr "Anfangsdatum" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:87 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:92 msgid "Ends at" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:105 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:110 msgid "Sold or future" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:130 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:135 msgid "" "\n" " Only use this to correct mistakes, i.e. if the share was\n" @@ -2420,19 +2439,19 @@ msgstr "" "Bitte nur verwenden, wenn ein tatsächlicher Fehler vorliegt, z.B. ein Anteil eingetragen wurde, der nie gekauft wurde. Wenn die Person ihren Anteil einfach an den Coop zurückverkauft hat, markiere den Anteil bitte als 'verkauft'.\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:153 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:158 msgid "Add Shares" msgstr "Anteile hinzufügen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 msgid "Willing to gift a share" msgstr "Bereit Anteile zu schenken" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:186 msgid "Send membership confirmation email" msgstr "Mitgliedsbestätigung per Mail senden" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:187 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:192 msgid "User is not a cooperative member." msgstr "Benutzer*in ist kein Genossenschaftsmitglied." @@ -2527,68 +2546,79 @@ msgstr "ohne Endddatum" msgid "Cancel membership of %(name)s" msgstr "Mitglied bearbeiten: %(name)s" -#: coop/views/shareowner.py:130 coop/views/shareowner.py:135 +#: coop/views/shareowner.py:133 coop/views/shareowner.py:138 #, python-format msgid "Edit share: %(name)s" msgstr "Anteil bearbeiten: %(name)s" -#: coop/views/shareowner.py:155 coop/views/shareowner.py:160 +#: coop/views/shareowner.py:158 coop/views/shareowner.py:163 #, python-format msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:374 +#: coop/views/shareowner.py:313 +#, fuzzy, python-format +#| msgid "Edit member: %(name)s" +msgid "Delete member: %(name)s" +msgstr "Mitglied bearbeiten: %(name)s" + +#: coop/views/shareowner.py:318 +#, python-format +msgid "Are you sure you want to delete: %(name)s?" +msgstr "" + +#: coop/views/shareowner.py:418 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:596 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:640 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:601 +#: coop/views/shareowner.py:645 #: shifts/templates/shifts/user_shifts_overview_tag.html:74 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:609 +#: coop/views/shareowner.py:653 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:617 +#: coop/views/shareowner.py:661 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:625 +#: coop/views/shareowner.py:669 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:633 +#: coop/views/shareowner.py:677 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:640 +#: coop/views/shareowner.py:684 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:643 +#: coop/views/shareowner.py:687 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:646 +#: coop/views/shareowner.py:690 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:650 +#: coop/views/shareowner.py:694 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:655 +#: coop/views/shareowner.py:699 #, fuzzy #| msgid "Shift missed" msgid "Shift Name" msgstr "Schicht versäumt" -#: coop/views/shareowner.py:929 +#: coop/views/shareowner.py:959 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" @@ -4525,10 +4555,6 @@ msgstr "Solidaritäts-Schicht spenden" msgid "One of your banked shifts will be donated as a solidarity shift. Do you want to continue?" msgstr "Eine deiner gearbeiteten Schichten wird als Solidaritäts-Schicht gespendet. Möchtest du fortfahren?" -#: shifts/templates/shifts/user_shifts_overview_tag.html:172 -msgid "Cancel" -msgstr "Abmelden" - #: shifts/templates/shifts/user_shifts_overview_tag.html:175 msgid "Confirm" msgstr "" From a89a54281eb0bc4f3989ae5dd2215d618a5ef52a Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 10:13:58 +0200 Subject: [PATCH 08/56] remove callback --- tapir/coop/views/shareowner.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 726e71f23..9486db243 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -302,9 +302,6 @@ def delete(self, request, *args, **kwargs): self.object = self.get_object() self.object.delete() - for callback in on_welcome_session_attendance_update: - callback(self.object) - return super().delete(request, *args, **kwargs) def get_context_data(self, **kwargs): From ea4534a3345f43ee7a7715dbb9e128a85a699e13 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 10:17:42 +0200 Subject: [PATCH 09/56] remove transaction atomic --- tapir/coop/views/shareowner.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 9486db243..017f99556 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -298,9 +298,7 @@ def get_success_url(self): return reverse("coop:shareowner_list") def delete(self, request, *args, **kwargs): - with transaction.atomic(): - self.object = self.get_object() - self.object.delete() + self.get_object().delete() return super().delete(request, *args, **kwargs) From 9cd2b394749b40907f82238ffc66d37381cd9c8b Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 10:32:45 +0200 Subject: [PATCH 10/56] use form_valid, according to docs https://docs.djangoproject.com/en/5.2/releases/4.0/#generic-views --- tapir/coop/views/shareowner.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 017f99556..196b1686f 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -297,10 +297,9 @@ def get_object(self, queryset=None): def get_success_url(self): return reverse("coop:shareowner_list") - def delete(self, request, *args, **kwargs): + def form_valid(self, form): self.get_object().delete() - - return super().delete(request, *args, **kwargs) + return super().form_valid(form) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) From c3f21fe26fd868cb461301f702e18de113d8f0b1 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 10:40:52 +0200 Subject: [PATCH 11/56] rename to soft_delete --- tapir/coop/models.py | 2 +- tapir/coop/tests/test_shareowner_softdelete.py | 18 ++++++++++++++---- tapir/coop/views/shareowner.py | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/tapir/coop/models.py b/tapir/coop/models.py index 258e11a61..e04516562 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -189,7 +189,7 @@ class Meta: everything = models.Manager() objects = NonDeleted() - def delete(self, using=None, keep_parents=False): + def soft_delete(self): self.deleted_at = timezone.now() self.save() diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index 8554c48cb..85c55624b 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -12,7 +12,7 @@ def test_softDelete_DeletedAt_isNotNone(self): assert share_owner.deleted_at is None - share_owner.delete() + share_owner.soft_delete() share_owner.refresh_from_db() @@ -22,7 +22,7 @@ def test_softDelete_DeletedAt_isNotNone(self): def test_softDelete_restoreDeleted_isNone(self): share_owner = ShareOwnerFactory.create() - share_owner.delete() + share_owner.soft_delete() share_owner.refresh_from_db() assert share_owner.deleted_at is not None @@ -35,7 +35,7 @@ def test_softDelete_nonDeletedManager_containsNotDeletedShareowners(self): active_owner = ShareOwnerFactory.create() soft_deleted_owner = ShareOwnerFactory.create() - soft_deleted_owner.delete() + soft_deleted_owner.soft_delete() non_deleted_owners = ShareOwner.objects.all() @@ -46,9 +46,19 @@ def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): owner1 = ShareOwnerFactory.create() owner2 = ShareOwnerFactory.create() - owner2.delete() + owner2.soft_delete() all_owners = ShareOwner.everything.all() assert owner1 in all_owners assert owner2 in all_owners + + def test_hardDelete_shareOwner_isfullyDeleted(self): + share_owner = ShareOwnerFactory.create() + all_users = ShareOwner.everything.all() + assert share_owner in all_users + + share_owner.delete() + + all_users = ShareOwner.everything.all() + assert share_owner not in all_users diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 196b1686f..de35cb27e 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -298,7 +298,7 @@ def get_success_url(self): return reverse("coop:shareowner_list") def form_valid(self, form): - self.get_object().delete() + self.get_object().soft_delete() return super().form_valid(form) def get_context_data(self, **kwargs): From 6668313cbb424625d748353c2004fc279ac11458 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:41:10 +0200 Subject: [PATCH 12/56] fix: fix failing tests. Reading again documentation helped, which stated "Therefore, you should not override get_queryset() to filter out any rows. If you do so, Django will return incomplete results." --- tapir/coop/models.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tapir/coop/models.py b/tapir/coop/models.py index e04516562..d914687b0 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -115,9 +115,7 @@ def with_status( class NonDeleted(models.Manager): def get_queryset(self): - return ShareOwnerQuerySet(self.model, using=self._db).filter( - deleted_at__isnull=True - ) + return super().get_queryset().filter(deleted_at__isnull=True) class ShareOwner(models.Model): @@ -187,7 +185,7 @@ class Meta: deleted_at = models.DateTimeField(null=True, blank=True) # Soft-Delete everything = models.Manager() - objects = NonDeleted() + objects = NonDeleted.from_queryset(ShareOwnerQuerySet)() def soft_delete(self): self.deleted_at = timezone.now() From 0a5215d127fb4da39e03a430301a9bf5671c247a Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:47:54 +0200 Subject: [PATCH 13/56] refactor: comply with self.assert... --- .../coop/tests/test_shareowner_softdelete.py | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index 85c55624b..4ee271926 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -10,26 +10,25 @@ class TestShareOwnershipSoftDelete(TapirFactoryTestBase): def test_softDelete_DeletedAt_isNotNone(self): share_owner = ShareOwnerFactory.create() - assert share_owner.deleted_at is None - + self.assertIsNone(share_owner.deleted_at) share_owner.soft_delete() share_owner.refresh_from_db() - assert share_owner.deleted_at is not None - assert share_owner.deleted_at <= timezone.now() + self.assertIsNotNone(share_owner.deleted_at) + self.assertLessEqual(share_owner.deleted_at, timezone.now()) def test_softDelete_restoreDeleted_isNone(self): share_owner = ShareOwnerFactory.create() share_owner.soft_delete() share_owner.refresh_from_db() - assert share_owner.deleted_at is not None + self.assertIsNotNone(share_owner.deleted_at) share_owner.restore() share_owner.refresh_from_db() - assert share_owner.deleted_at is None + self.assertIsNone(share_owner.deleted_at) def test_softDelete_nonDeletedManager_containsNotDeletedShareowners(self): active_owner = ShareOwnerFactory.create() @@ -38,9 +37,8 @@ def test_softDelete_nonDeletedManager_containsNotDeletedShareowners(self): soft_deleted_owner.soft_delete() non_deleted_owners = ShareOwner.objects.all() - - assert active_owner in non_deleted_owners - assert soft_deleted_owner not in non_deleted_owners + self.assertIn(active_owner, non_deleted_owners) + self.assertNotIn(soft_deleted_owner, non_deleted_owners) def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): owner1 = ShareOwnerFactory.create() @@ -50,15 +48,14 @@ def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): all_owners = ShareOwner.everything.all() - assert owner1 in all_owners - assert owner2 in all_owners + self.assertIn(owner1, all_owners) + self.assertIn(owner2, all_owners) def test_hardDelete_shareOwner_isfullyDeleted(self): share_owner = ShareOwnerFactory.create() all_users = ShareOwner.everything.all() - assert share_owner in all_users - + self.assertIn(share_owner, all_users) share_owner.delete() all_users = ShareOwner.everything.all() - assert share_owner not in all_users + self.assertNotIn(share_owner, all_users) From d606f36ae2458565af7e45b0811ec95ae024630c Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:49:07 +0200 Subject: [PATCH 14/56] use outline-danger --- .../templates/coop/tags/user_coop_share_ownership_list_tag.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html index cc77dbef2..3a34762f5 100644 --- a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html +++ b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html @@ -21,7 +21,7 @@
edit{% translate 'Edit' %} - delete{% translate 'Delete' %} From 94eadc3032a0635ef1bb0633ffcb9575e44fb2c3 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:58:15 +0200 Subject: [PATCH 15/56] move part of forbidding removing own user to form_valid --- tapir/coop/views/shareowner.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index de35cb27e..cb40285b4 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -289,15 +289,15 @@ class ShareOwnerDeleteView( model = ShareOwner def get_object(self, queryset=None): - share_owner = super().get_object(queryset) - if self.request.user == share_owner.user: - raise PermissionDenied("You cannot delete your own account.") - return share_owner + return super().get_object(queryset) def get_success_url(self): return reverse("coop:shareowner_list") def form_valid(self, form): + share_owner = self.get_object() + if self.request.user == share_owner.user: + raise PermissionDenied("You cannot delete your own account.") self.get_object().soft_delete() return super().form_valid(form) From 0ca1fb06da4440931c9736c4e4d4bff47915473b Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 12:58:29 +0200 Subject: [PATCH 16/56] test: forbidding removing own user --- tapir/coop/tests/test_shareowner_softdelete.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index 4ee271926..05c049198 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -1,6 +1,8 @@ import pytest +from django.urls import reverse from django.utils import timezone +from tapir.accounts.tests.factories.factories import TapirUserFactory from tapir.coop.models import ShareOwner from tapir.coop.tests.factories import ShareOwnerFactory from tapir.utils.tests_utils import TapirFactoryTestBase @@ -59,3 +61,11 @@ def test_hardDelete_shareOwner_isfullyDeleted(self): all_users = ShareOwner.everything.all() self.assertNotIn(share_owner, all_users) + + def test_delete_cannotDeleteOwnAccount(self): + vorstand_user = self.login_as_vorstand() + response = self.client.post( + reverse("coop:shareowner_delete", args=[vorstand_user.share_owner.id]) + ) + self.assertEqual(response.status_code, 403) + self.assertIn(vorstand_user.share_owner, ShareOwner.everything.all()) From 583728882f79e3f66fc7e4d81f7bd9dcb927947b Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 10 Oct 2025 13:36:50 +0200 Subject: [PATCH 17/56] access only to perms.group.manage --- .../user_coop_share_ownership_list_tag.html | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html index 3a34762f5..6a414a43f 100644 --- a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html +++ b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html @@ -10,22 +10,26 @@
- - file_present{% translate 'Membership confirmation' %} - - - edit{% translate 'Edit' %} - - - delete{% translate 'Delete' %} - + {% if perms.accounts.manage %} + + file_present{% translate 'Membership confirmation' %} + + + edit{% translate 'Edit' %} + + {% endif %} + {% if perms.group.manage %} + + delete{% translate 'Delete' %} + + {% endif %} {% endif %} {% endif %} From 1a179c8ed02bb86f91019c84908deea5f8389cff Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 12 Oct 2025 08:25:24 +0200 Subject: [PATCH 18/56] translation --- .../locale/de/LC_MESSAGES/django.po | 194 +++++++++--------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index 8ea4fd907..ff0ea94b6 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-05 17:43+0200\n" +"POT-Creation-Date: 2025-10-12 08:24+0200\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -56,12 +56,12 @@ msgstr "Wichtige E-Mails" msgid "Displayed name" msgstr "Angezeigter Name" -#: accounts/models.py:70 coop/models.py:159 coop/models.py:465 +#: accounts/models.py:70 coop/models.py:157 coop/models.py:463 msgid "Pronouns" msgstr "Pronomen" #: accounts/models.py:71 accounts/templates/accounts/user_detail.html:71 -#: coop/models.py:161 coop/models.py:467 +#: coop/models.py:159 coop/models.py:465 #: coop/templates/coop/draftuser_detail.html:71 #: coop/templates/coop/draftuser_detail.html:142 #: coop/templates/coop/shareowner_detail.html:51 @@ -69,29 +69,29 @@ msgid "Phone number" msgstr "Telefonnummer" #: accounts/models.py:72 accounts/templates/accounts/user_detail.html:81 -#: coop/models.py:162 coop/models.py:468 +#: coop/models.py:160 coop/models.py:466 #: coop/templates/coop/draftuser_detail.html:146 #: coop/templates/coop/shareowner_detail.html:55 msgid "Birthdate" msgstr "Geburtsdatum" -#: accounts/models.py:73 coop/models.py:163 coop/models.py:469 +#: accounts/models.py:73 coop/models.py:161 coop/models.py:467 msgid "Street and house number" msgstr "Straße und Hausnummer" -#: accounts/models.py:74 coop/models.py:164 coop/models.py:470 +#: accounts/models.py:74 coop/models.py:162 coop/models.py:468 msgid "Extra address line" msgstr "Adresszusatz" -#: accounts/models.py:75 coop/models.py:165 coop/models.py:471 +#: accounts/models.py:75 coop/models.py:163 coop/models.py:469 msgid "Postcode" msgstr "Postleitzahl" -#: accounts/models.py:76 coop/models.py:166 coop/models.py:472 +#: accounts/models.py:76 coop/models.py:164 coop/models.py:470 msgid "City" msgstr "Ort" -#: accounts/models.py:77 coop/models.py:167 coop/models.py:473 +#: accounts/models.py:77 coop/models.py:165 coop/models.py:471 msgid "Country" msgstr "Land" @@ -120,7 +120,7 @@ msgid "Allow purchase tracking" msgstr "Erlaube Aufzeichnen deiner Einkäufe" #: accounts/models.py:90 accounts/templates/accounts/user_detail.html:101 -#: coop/models.py:170 coop/models.py:476 +#: coop/models.py:168 coop/models.py:474 #: coop/templates/coop/shareowner_detail.html:75 msgid "Preferred Language" msgstr "Bevorzugte Sprache" @@ -279,8 +279,8 @@ msgid "I agree that my membership card will be scanned at the checkout when I ma msgstr "Ich bin damit einverstanden, dass meine Mitgliedskarte beim Einkauf an der Kasse gescannt und somit mein Einkauf erfasst und gespeichert wird:" #: accounts/templates/accounts/purchase_tracking_card.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:173 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:177 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 msgid "Yes,No" msgstr "Ja, Nein" @@ -299,7 +299,7 @@ msgstr "Aktivieren" msgid "You can only look at your own barcode unless you have admin rights" msgstr "Du kannst nur deinen eigenen Strichcode sehen, es sei denn du hast Admin-Rechte" -#: accounts/templates/accounts/user_detail.html:20 coop/models.py:744 +#: accounts/templates/accounts/user_detail.html:20 coop/models.py:742 #: coop/templates/coop/draftuser_detail.html:86 #: coop/templates/coop/shareowner_detail.html:10 log/views.py:94 #: log/views.py:152 shifts/templates/shifts/shift_day_printable.html:55 @@ -330,9 +330,9 @@ msgstr "Benutzername bearbeiten" #: coop/templates/coop/draftuser_detail.html:238 #: coop/templates/coop/membershipresignation_detail.html:107 #: coop/templates/coop/shareowner_detail.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:22 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:95 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:118 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:23 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:99 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:122 #: core/templates/core/featureflag_list.html:30 #: shifts/templates/shifts/shift_detail.html:60 #: shifts/templates/shifts/shift_template_detail.html:25 @@ -641,7 +641,7 @@ msgstr "Anzahl zu erstellender Anteile" msgid "The end date must be later than the start date." msgstr "Das Enddatum muss später als das Start-Datum sein" -#: coop/forms.py:105 coop/models.py:482 +#: coop/forms.py:105 coop/models.py:480 msgid "Number of Shares" msgstr "Anzahl Anteile" @@ -726,172 +726,172 @@ msgstr "Absender und Empfänger der Übertragung der Anteile können nicht ident msgid "Cannot pay out, because shares have been gifted." msgstr "Die Anteile können nicht ausgezahlt werden, da sie verschenkt wurden" -#: coop/models.py:143 +#: coop/models.py:141 msgid "Is company" msgstr "Ist eine Firma" -#: coop/models.py:150 coop/models.py:456 +#: coop/models.py:148 coop/models.py:454 msgid "Administrative first name" msgstr "Amtlicher Vorname" -#: coop/models.py:152 coop/models.py:458 +#: coop/models.py:150 coop/models.py:456 msgid "Last name" msgstr "Nachname" -#: coop/models.py:154 coop/models.py:460 +#: coop/models.py:152 coop/models.py:458 msgid "Usage name" msgstr "Angezeigter Name" -#: coop/models.py:160 coop/models.py:466 +#: coop/models.py:158 coop/models.py:464 msgid "Email address" msgstr "E-Mail-Adresse" -#: coop/models.py:177 +#: coop/models.py:175 msgid "Is investing member" msgstr "Ist investierendes Mitglied" -#: coop/models.py:179 coop/models.py:496 +#: coop/models.py:177 coop/models.py:494 #: coop/templates/coop/draftuser_detail.html:176 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:53 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:172 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:57 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 msgid "Ratenzahlung" msgstr "Ratenzahlung" -#: coop/models.py:181 coop/models.py:489 +#: coop/models.py:179 coop/models.py:487 msgid "Attended Welcome Session" msgstr "An Willkommenstreffen teilgenommen" -#: coop/models.py:184 +#: coop/models.py:182 msgid "Is willing to gift a share" msgstr "Ist bereit Anteile zu verschenken" -#: coop/models.py:219 +#: coop/models.py:217 msgid "Cannot be a company and have a Tapir account" msgstr "Kann keine Firma sein und ein Tapir-Konto haben" -#: coop/models.py:235 +#: coop/models.py:233 msgid "User info should be stored in associated Tapir account" msgstr "Benutzer Infos sollen in dem Tapir Konto gespeichert warden" -#: coop/models.py:387 +#: coop/models.py:385 msgid "Not a member" msgstr "Kein Mitlied" -#: coop/models.py:388 coop/templates/coop/draftuser_detail.html:169 +#: coop/models.py:386 coop/templates/coop/draftuser_detail.html:169 msgid "Investing" msgstr "Investierend" -#: coop/models.py:389 coop/templates/coop/draftuser_detail.html:171 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:108 +#: coop/models.py:387 coop/templates/coop/draftuser_detail.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:112 #: coop/views/statistics.py:135 msgid "Active" msgstr "Aktiv" -#: coop/models.py:390 +#: coop/models.py:388 msgid "Paused" msgstr "Pausiert" -#: coop/models.py:485 +#: coop/models.py:483 msgid "Investing member" msgstr "Investierendes Mitglied" -#: coop/models.py:492 +#: coop/models.py:490 msgid "Signed Beteiligungserklärung" msgstr "Beteiligungserklärung unterschrieben" -#: coop/models.py:494 +#: coop/models.py:492 msgid "Paid Entrance Fee" msgstr "Eintrittsgeld bezahlt" -#: coop/models.py:551 +#: coop/models.py:549 msgid "Email address must be set." msgstr "Email-Adresse muss gesetzt sein." -#: coop/models.py:553 +#: coop/models.py:551 msgid "First name must be set." msgstr "Vorname muss gesetzt sein." -#: coop/models.py:555 +#: coop/models.py:553 msgid "Last name must be set." msgstr "Nachname muss gesetzt sein." -#: coop/models.py:559 +#: coop/models.py:557 msgid "Membership agreement must be signed." msgstr "Mitgliedsantrag muss unterschrieben sein." -#: coop/models.py:561 +#: coop/models.py:559 msgid "Amount of requested shares must be positive." msgstr "Die Anzahl der erwünschten Anteile muss positiv sein." -#: coop/models.py:563 +#: coop/models.py:561 msgid "Member already created." msgstr "Mitglied schon vorhanden." -#: coop/models.py:590 +#: coop/models.py:588 msgid "Paying member" msgstr "Zahlendes Mitglied" -#: coop/models.py:598 +#: coop/models.py:596 msgid "Credited member" msgstr "Empfangendes Mitglied" -#: coop/models.py:605 +#: coop/models.py:603 msgid "Amount" msgstr "Betrag" -#: coop/models.py:611 +#: coop/models.py:609 msgid "Payment date" msgstr "Zahldatum" -#: coop/models.py:614 +#: coop/models.py:612 msgid "Creation date" msgstr "Erstellungsdatum" -#: coop/models.py:619 +#: coop/models.py:617 msgid "Created by" msgstr "Erstellt durch" -#: coop/models.py:794 +#: coop/models.py:792 msgid "The cooperative buys the shares back from the member" msgstr "Die Kooperative kauft die Anteile des Mitglieds zurück." -#: coop/models.py:797 +#: coop/models.py:795 #, fuzzy #| msgid "Number of shares to create" msgid "The member gifts the shares to the cooperative" msgstr "Anzahl zu erstellender Anteile" -#: coop/models.py:799 +#: coop/models.py:797 msgid "The shares get transferred to another member" msgstr "Die Anteile werden an ein anderes Mitglied übertragen" -#: coop/models.py:802 +#: coop/models.py:800 msgid "Financial reasons" msgstr "Finanzielle Gründe" -#: coop/models.py:803 +#: coop/models.py:801 msgid "Health reasons" msgstr "Gesundheit" -#: coop/models.py:804 +#: coop/models.py:802 msgid "Distance" msgstr "Entfernung" -#: coop/models.py:805 +#: coop/models.py:803 msgid "Strategic orientation of SuperCoop" msgstr "Strategische Ausrichtung von Supercoop" -#: coop/models.py:806 +#: coop/models.py:804 msgid "Other" msgstr "Andere" -#: coop/models.py:811 +#: coop/models.py:809 #, fuzzy #| msgid "Edit shareowner" msgid "Shareowner" msgstr "Mitglied bearbeiten" -#: coop/models.py:830 +#: coop/models.py:828 msgid "Leave this empty if the resignation type is not a transfer to another member" msgstr "Lass das Feld leer, wenn es sich nicht um eine Übertragung auf ein anderes Mitglied handelt" @@ -932,8 +932,8 @@ msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 #: coop/templates/coop/confirm_delete_share_ownership.html:24 #: coop/templates/coop/shareowner_confirm_delete.html:22 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:27 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:132 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:30 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:136 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 #: shifts/templates/shifts/shift_confirm_delete.html:30 msgid "Delete" @@ -999,9 +999,9 @@ msgid "List of similar members" msgstr "Liste alle E-Mails" #: coop/templates/coop/draftuser_detail.html:69 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:36 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:93 -#: coop/views/shareowner.py:639 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 +#: coop/views/shareowner.py:633 #: shifts/templates/shifts/user_shifts_overview_tag.html:21 msgid "Status" msgstr "Status" @@ -1024,7 +1024,7 @@ msgid "Shares requested" msgstr "Angeforderte Anteile" #: coop/templates/coop/draftuser_detail.html:190 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:62 #: shifts/models.py:44 msgid "Welcome Session" msgstr "Willkommenstreffen" @@ -1978,7 +1978,7 @@ msgid "General Tapir Accounts" msgstr "Tapir-Konto erstellen" #: coop/templates/coop/incoming_payment_list.html:9 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:165 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:169 msgid "Payments" msgstr "Zahlungen" @@ -2369,11 +2369,11 @@ msgstr "" " Mitglied #%(coop_share_owner_id)s\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:17 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:18 msgid "Membership confirmation" msgstr "Mitgliedsbestätigung" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:44 #, python-format msgid "" "\n" @@ -2381,7 +2381,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:45 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:49 #, python-format msgid "" "\n" @@ -2389,43 +2389,43 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:61 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 #: shifts/models.py:955 shifts/templates/shifts/shift_day_printable.html:56 #: shifts/templates/shifts/shift_detail.html:297 #: shifts/templates/shifts/shift_detail_printable.html:51 msgid "Attended" msgstr "Teilgenommen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:63 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:67 #: shifts/models.py:954 msgid "Pending" msgstr "Ausstehend" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:70 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 msgid "Mark Attended" msgstr "Als teilgenommen markieren" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:79 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:83 msgid "Owned shares" msgstr "Anteile" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:87 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:91 msgid "List of shares owned by this member" msgstr "Liste der Anteile die dieses Mitglied gehören" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:91 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:95 msgid "Starts at" msgstr "Anfangsdatum" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:92 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:96 msgid "Ends at" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:110 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:114 msgid "Sold or future" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:135 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:139 msgid "" "\n" " Only use this to correct mistakes, i.e. if the share was\n" @@ -2439,19 +2439,19 @@ msgstr "" "Bitte nur verwenden, wenn ein tatsächlicher Fehler vorliegt, z.B. ein Anteil eingetragen wurde, der nie gekauft wurde. Wenn die Person ihren Anteil einfach an den Coop zurückverkauft hat, markiere den Anteil bitte als 'verkauft'.\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:158 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:162 msgid "Add Shares" msgstr "Anteile hinzufügen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:180 msgid "Willing to gift a share" msgstr "Bereit Anteile zu schenken" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:186 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:190 msgid "Send membership confirmation email" msgstr "Mitgliedsbestätigung per Mail senden" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:192 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:196 msgid "User is not a cooperative member." msgstr "Benutzer*in ist kein Genossenschaftsmitglied." @@ -2556,69 +2556,69 @@ msgstr "Anteil bearbeiten: %(name)s" msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:313 +#: coop/views/shareowner.py:307 #, fuzzy, python-format #| msgid "Edit member: %(name)s" msgid "Delete member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" -#: coop/views/shareowner.py:318 +#: coop/views/shareowner.py:312 #, python-format msgid "Are you sure you want to delete: %(name)s?" msgstr "" -#: coop/views/shareowner.py:418 +#: coop/views/shareowner.py:412 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:640 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:634 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:645 +#: coop/views/shareowner.py:639 #: shifts/templates/shifts/user_shifts_overview_tag.html:74 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:653 +#: coop/views/shareowner.py:647 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:661 +#: coop/views/shareowner.py:655 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:669 +#: coop/views/shareowner.py:663 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:677 +#: coop/views/shareowner.py:671 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:684 +#: coop/views/shareowner.py:678 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:687 +#: coop/views/shareowner.py:681 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:690 +#: coop/views/shareowner.py:684 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:694 +#: coop/views/shareowner.py:688 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:699 +#: coop/views/shareowner.py:693 #, fuzzy #| msgid "Shift missed" msgid "Shift Name" msgstr "Schicht versäumt" -#: coop/views/shareowner.py:959 +#: coop/views/shareowner.py:953 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" From add0fe3314d9e44eda8033765c21fff7b57438c4 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Mon, 13 Oct 2025 10:31:44 +0200 Subject: [PATCH 19/56] avoid auto-escaping --- tapir/coop/templates/coop/shareowner_confirm_delete.html | 2 +- tapir/coop/views/shareowner.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tapir/coop/templates/coop/shareowner_confirm_delete.html b/tapir/coop/templates/coop/shareowner_confirm_delete.html index e0bd2496d..4926fdabc 100644 --- a/tapir/coop/templates/coop/shareowner_confirm_delete.html +++ b/tapir/coop/templates/coop/shareowner_confirm_delete.html @@ -12,7 +12,7 @@

{{ page_title }}

Are you sure you want to delete this member?

- Name: {{ card_title|safe }} + Name: {{ card_title }}

diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index cb40285b4..c62004b67 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -310,7 +310,7 @@ def get_context_data(self, **kwargs): ) } context["card_title"] = _("Are you sure you want to delete: %(name)s?") % { - "name": UserUtils.build_html_link_for_viewer(share_owner, self.request.user) + "name": share_owner } return context From f9cdf1d3fb1e9f50f00f6a960b7d2389518c2c43 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:55:09 +0200 Subject: [PATCH 20/56] reformat --- .../coop/confirm_delete_share_ownership.html | 33 ++++++++-------- .../coop/shareowner_confirm_delete.html | 38 +++++++++++-------- tapir/coop/views/shareowner.py | 13 ------- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/tapir/coop/templates/coop/confirm_delete_share_ownership.html b/tapir/coop/templates/coop/confirm_delete_share_ownership.html index 534a3ed1c..68f5ded0a 100644 --- a/tapir/coop/templates/coop/confirm_delete_share_ownership.html +++ b/tapir/coop/templates/coop/confirm_delete_share_ownership.html @@ -11,21 +11,22 @@
{% translate "Confirm delete" %}
-
- - {% csrf_token %} -

- Are you sure you want to delete the following share? {{ object }} -

- {{ form }} -
- -
- +
+
+
+ {% csrf_token %} +

+ Are you sure you want to delete the following share? {{ object }} +

+ {{ form }} +
+ +
+
+
- -{% endblock content %} + {% endblock content %} diff --git a/tapir/coop/templates/coop/shareowner_confirm_delete.html b/tapir/coop/templates/coop/shareowner_confirm_delete.html index 4926fdabc..6ab042096 100644 --- a/tapir/coop/templates/coop/shareowner_confirm_delete.html +++ b/tapir/coop/templates/coop/shareowner_confirm_delete.html @@ -4,25 +4,31 @@ {% load i18n %} {% load core %} {% block title %} - {% translate "Confirm delete" %} {{ object }} + {% translate "Confirm Delete" %}: {% get_display_name_for_viewer object request.user %} {% endblock title %} {% block content %}
-

{{ page_title }}

-
-

Are you sure you want to delete this member?

-

- Name: {{ card_title }} -

+
+
+ {% translate "Confirm Delete" %}: {% get_display_name_for_viewer object request.user %} +
+
+
+

Are you sure you want to delete this member?

+

+ Name: {{ object }} +

+
+
+ {% csrf_token %} + + {% translate 'Cancel' %} +
+
-
- {% csrf_token %} - - {% translate 'Cancel' %} -
{% endblock content %} diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index c62004b67..3fcb8e47a 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -301,19 +301,6 @@ def form_valid(self, form): self.get_object().soft_delete() return super().form_valid(form) - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - share_owner = self.object - context["page_title"] = _("Delete member: %(name)s") % { - "name": UserUtils.build_display_name_for_viewer( - share_owner, self.request.user - ) - } - context["card_title"] = _("Are you sure you want to delete: %(name)s?") % { - "name": share_owner - } - return context - @require_POST @csrf_protect From 6a58c51fc2995ce65b5d2059f08a669bda453711 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:35:48 +0200 Subject: [PATCH 21/56] Permission_group_manage --- tapir/coop/views/shareowner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 3fcb8e47a..68c092bf9 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -76,6 +76,7 @@ PERMISSION_COOP_ADMIN, PERMISSION_ACCOUNTS_MANAGE, PERMISSION_COOP_VIEW, + PERMISSION_GROUP_MANAGE, ) from tapir.shifts.models import ( SHIFT_USER_CAPABILITY_CHOICES, @@ -285,7 +286,7 @@ class ShareOwnerDeleteView( PermissionRequiredMixin, generic.DeleteView, ): - permission_required = PERMISSION_ACCOUNTS_MANAGE + permission_required = PERMISSION_GROUP_MANAGE model = ShareOwner def get_object(self, queryset=None): From 9a125c8e357e6a68d80af3f27efdad387ee3f17a Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:45:21 +0200 Subject: [PATCH 22/56] add simple test --- .../coop/tests/test_shareowner_delete_view.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tapir/coop/tests/test_shareowner_delete_view.py diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py new file mode 100644 index 000000000..eeb4c5a21 --- /dev/null +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -0,0 +1,29 @@ +from django.urls import reverse + +from tapir import settings +from tapir.accounts.tests.factories.factories import TapirUserFactory +from tapir.utils.tests_utils import ( + PermissionTestMixin, + FeatureFlagTestMixin, + TapirFactoryTestBase, +) + + +class TestShareOwnerDeleteView( + PermissionTestMixin, FeatureFlagTestMixin, TapirFactoryTestBase +): + def setUp(self) -> None: + super().setUp() + + def get_allowed_groups(self): + return [ + settings.GROUP_VORSTAND, + settings.GROUP_EMPLOYEES, + ] + + def do_request(self): + tapir_user = TapirUserFactory(allows_purchase_tracking=True) + return self.client.post( + reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), + follow=True, + ) From bc948ce1cf92a4e8c2600c8c07916bd48770f9ad Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sat, 18 Oct 2025 21:07:07 +0200 Subject: [PATCH 23/56] translation --- .../locale/de/LC_MESSAGES/django.po | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index ff0ea94b6..9b22df79d 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-12 08:24+0200\n" +"POT-Creation-Date: 2025-10-18 21:06+0200\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -523,8 +523,8 @@ msgstr "Schickt mir eine Anleitung!" msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters." msgstr "Der angegebene Name ist ungültig. Er darf nur Buchstaben, Zahlen und die Symbole ./-/_ enthalten." -#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:272 -#: coop/views/shareowner.py:277 +#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:273 +#: coop/views/shareowner.py:278 #, python-format msgid "Edit member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" @@ -923,15 +923,14 @@ msgstr "Aktive Mitglieder mit Tapir-Konto" #: coop/templates/coop/confirm_delete_share_ownership.html:6 #: coop/templates/coop/confirm_delete_share_ownership.html:12 #: coop/templates/coop/draftuser_detail.html:20 -#: coop/templates/coop/shareowner_confirm_delete.html:7 #: financingcampaign/templates/financingcampaign/confirm_delete.html:6 #: financingcampaign/templates/financingcampaign/confirm_delete.html:12 msgid "Confirm delete" msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 -#: coop/templates/coop/confirm_delete_share_ownership.html:24 -#: coop/templates/coop/shareowner_confirm_delete.html:22 +#: coop/templates/coop/confirm_delete_share_ownership.html:25 +#: coop/templates/coop/shareowner_confirm_delete.html:26 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:30 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:136 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 @@ -1001,7 +1000,7 @@ msgstr "Liste alle E-Mails" #: coop/templates/coop/draftuser_detail.html:69 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 -#: coop/views/shareowner.py:633 +#: coop/views/shareowner.py:621 #: shifts/templates/shifts/user_shifts_overview_tag.html:21 msgid "Status" msgstr "Status" @@ -2282,7 +2281,14 @@ msgstr "" msgid "Organisation logo" msgstr "" -#: coop/templates/coop/shareowner_confirm_delete.html:25 +#: coop/templates/coop/shareowner_confirm_delete.html:7 +#: coop/templates/coop/shareowner_confirm_delete.html:13 +#, fuzzy +#| msgid "Confirm delete" +msgid "Confirm Delete" +msgstr "Löschen bestätigen" + +#: coop/templates/coop/shareowner_confirm_delete.html:29 #: shifts/templates/shifts/user_shifts_overview_tag.html:172 msgid "Cancel" msgstr "Abmelden" @@ -2546,79 +2552,68 @@ msgstr "ohne Endddatum" msgid "Cancel membership of %(name)s" msgstr "Mitglied bearbeiten: %(name)s" -#: coop/views/shareowner.py:133 coop/views/shareowner.py:138 +#: coop/views/shareowner.py:134 coop/views/shareowner.py:139 #, python-format msgid "Edit share: %(name)s" msgstr "Anteil bearbeiten: %(name)s" -#: coop/views/shareowner.py:158 coop/views/shareowner.py:163 +#: coop/views/shareowner.py:159 coop/views/shareowner.py:164 #, python-format msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:307 -#, fuzzy, python-format -#| msgid "Edit member: %(name)s" -msgid "Delete member: %(name)s" -msgstr "Mitglied bearbeiten: %(name)s" - -#: coop/views/shareowner.py:312 -#, python-format -msgid "Are you sure you want to delete: %(name)s?" -msgstr "" - -#: coop/views/shareowner.py:412 +#: coop/views/shareowner.py:400 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:634 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:622 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:639 +#: coop/views/shareowner.py:627 #: shifts/templates/shifts/user_shifts_overview_tag.html:74 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:647 +#: coop/views/shareowner.py:635 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:655 +#: coop/views/shareowner.py:643 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:663 +#: coop/views/shareowner.py:651 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:671 +#: coop/views/shareowner.py:659 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:678 +#: coop/views/shareowner.py:666 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:681 +#: coop/views/shareowner.py:669 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:684 +#: coop/views/shareowner.py:672 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:688 +#: coop/views/shareowner.py:676 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:693 +#: coop/views/shareowner.py:681 #, fuzzy #| msgid "Shift missed" msgid "Shift Name" msgstr "Schicht versäumt" -#: coop/views/shareowner.py:953 +#: coop/views/shareowner.py:941 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" @@ -6173,6 +6168,11 @@ msgstr "%(name)s ist kein Mitglied der Genossenschaft. Vielleicht haben sie dere msgid "%(name)s has not attended a welcome session yet. Make sure they plan to do it!" msgstr "%(name)s hat an dem Willkommenstreffen noch nicht teilgenommen. Stelle sicher, dass er*sie es entsprechend einplant!" +#, fuzzy, python-format +#~| msgid "Edit member: %(name)s" +#~ msgid "Delete member: %(name)s" +#~ msgstr "Mitglied bearbeiten: %(name)s" + #~ msgid "Purchase tracking" #~ msgstr "Vergangene Einkäufe" From 4e096943126fc289284eac13b87a18874adedb99 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:40:36 +0100 Subject: [PATCH 24/56] added more information about the consequences of deleting. --- .../coop/templates/coop/shareowner_confirm_delete.html | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tapir/coop/templates/coop/shareowner_confirm_delete.html b/tapir/coop/templates/coop/shareowner_confirm_delete.html index 6ab042096..5e73c4dde 100644 --- a/tapir/coop/templates/coop/shareowner_confirm_delete.html +++ b/tapir/coop/templates/coop/shareowner_confirm_delete.html @@ -13,12 +13,10 @@
{% translate "Confirm Delete" %}: {% get_display_name_for_viewer object request.user %}
-
-

Are you sure you want to delete this member?

-

- Name: {{ object }} -

-
+
{% translate "Are you sure you want to delete this member?" %}
+

+ {% translate "Please note that deleting this member will permanently remove their account and all associated data such as owned shares and payments. The deleted user will no longer be part of the cooperative. However, all shift records and logs will remain intact." %} +

{% csrf_token %} {% translate 'Cancel' %} + class="{% tapir_button_link %}">{% translate 'Cancel' %}
From 30049e9d807bb75af3a58d0db9f79da6e6cb8293 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:47:21 +0100 Subject: [PATCH 26/56] undo changes by mistake --- .../coop/confirm_delete_share_ownership.html | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/tapir/coop/templates/coop/confirm_delete_share_ownership.html b/tapir/coop/templates/coop/confirm_delete_share_ownership.html index 68f5ded0a..534a3ed1c 100644 --- a/tapir/coop/templates/coop/confirm_delete_share_ownership.html +++ b/tapir/coop/templates/coop/confirm_delete_share_ownership.html @@ -11,22 +11,21 @@
{% translate "Confirm delete" %}
-
-
-
- {% csrf_token %} -

- Are you sure you want to delete the following share? {{ object }} -

- {{ form }} -
- -
-
-
+
+
+ {% csrf_token %} +

+ Are you sure you want to delete the following share? {{ object }} +

+ {{ form }} +
+ +
+
- {% endblock content %} + +{% endblock content %} From e8244b2231490ec49d894677909d124f9fdff758 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 16 Nov 2025 15:06:30 +0100 Subject: [PATCH 27/56] add logs --- .../0057_deleteshareownerlogentry.py | 37 +++++++++++++++++++ tapir/coop/models.py | 7 ++++ .../log/delete_share_owner_log_entry.html | 4 ++ tapir/coop/views/shareowner.py | 6 +++ 4 files changed, 54 insertions(+) create mode 100644 tapir/coop/migrations/0057_deleteshareownerlogentry.py create mode 100644 tapir/coop/templates/coop/log/delete_share_owner_log_entry.html diff --git a/tapir/coop/migrations/0057_deleteshareownerlogentry.py b/tapir/coop/migrations/0057_deleteshareownerlogentry.py new file mode 100644 index 000000000..90044b160 --- /dev/null +++ b/tapir/coop/migrations/0057_deleteshareownerlogentry.py @@ -0,0 +1,37 @@ +# Generated by Django 5.1.12 on 2025-11-16 14:01 + +import django.contrib.postgres.fields.hstore +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("coop", "0056_alter_shareowner_managers_shareowner_deleted_at"), + ("log", "0008_logentry_log_logentr_user_id_c4ca60_idx_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="DeleteShareOwnerLogEntry", + fields=[ + ( + "logentry_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="log.logentry", + ), + ), + ("values", django.contrib.postgres.fields.hstore.HStoreField()), + ], + options={ + "abstract": False, + }, + bases=("log.logentry",), + ), + ] diff --git a/tapir/coop/models.py b/tapir/coop/models.py index d914687b0..b0f481eec 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -884,3 +884,10 @@ def populate( return super().populate_base( actor=actor, share_owner=model.share_owner, model=model ) + + +class DeleteShareOwnerLogEntry(ModelLogEntry): + template_name = "coop/log/delete_share_owner_log_entry.html" + + def populate(self, share_owner: ShareOwner, actor, model): + return self.populate_base(share_owner=share_owner, actor=actor, model=model) diff --git a/tapir/coop/templates/coop/log/delete_share_owner_log_entry.html b/tapir/coop/templates/coop/log/delete_share_owner_log_entry.html new file mode 100644 index 000000000..4667c7a7c --- /dev/null +++ b/tapir/coop/templates/coop/log/delete_share_owner_log_entry.html @@ -0,0 +1,4 @@ +{% load i18n %} +{% blocktranslate %} +Member has been deleted. +{% endblocktranslate %} diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 68c092bf9..64f9b8ae4 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -60,6 +60,7 @@ UpdateShareOwnershipLogEntry, ExtraSharesForAccountingRecap, ShareOwnerQuerySet, + DeleteShareOwnerLogEntry, ) from tapir.coop.services.investing_status_service import InvestingStatusService from tapir.coop.services.membership_pause_service import MembershipPauseService @@ -300,6 +301,11 @@ def form_valid(self, form): if self.request.user == share_owner.user: raise PermissionDenied("You cannot delete your own account.") self.get_object().soft_delete() + DeleteShareOwnerLogEntry().populate( + share_owner=share_owner, + actor=self.request.user, + model=self.object, + ).save() return super().form_valid(form) From 1676bdca836138f59b50d9603a4a85667b6e4a8d Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:26:03 +0100 Subject: [PATCH 28/56] test_shareOwnerGetsDeleted_deletedAt_hasDate --- tapir/coop/tests/test_shareowner_delete_view.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py index eeb4c5a21..b58102464 100644 --- a/tapir/coop/tests/test_shareowner_delete_view.py +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -1,7 +1,10 @@ +from http import HTTPStatus + from django.urls import reverse from tapir import settings from tapir.accounts.tests.factories.factories import TapirUserFactory +from tapir.coop.models import DeleteShareOwnerLogEntry from tapir.utils.tests_utils import ( PermissionTestMixin, FeatureFlagTestMixin, @@ -22,8 +25,20 @@ def get_allowed_groups(self): ] def do_request(self): - tapir_user = TapirUserFactory(allows_purchase_tracking=True) + tapir_user = TapirUserFactory() return self.client.post( reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), follow=True, ) + + def test_shareOwnerGetsDeleted_deletedAt_hasDate(self): + self.login_as_vorstand() + tapir_user = TapirUserFactory() + response = self.client.post( + reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), + follow=True, + ) + self.assertStatusCode(response, HTTPStatus.OK) + print(tapir_user.share_owner.deleted_at) + self.assertIsNotNone(tapir_user.share_owner.deleted_at) + self.assertEqual(DeleteShareOwnerLogEntry.objects.count(), 1) From fa6cf2b7738e05fa327ceba00d5506fd457442d3 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:34:30 +0100 Subject: [PATCH 29/56] fix: test_shareowner_delete_view.py --- tapir/coop/tests/test_shareowner_delete_view.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py index b58102464..8559627e8 100644 --- a/tapir/coop/tests/test_shareowner_delete_view.py +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -39,6 +39,7 @@ def test_shareOwnerGetsDeleted_deletedAt_hasDate(self): follow=True, ) self.assertStatusCode(response, HTTPStatus.OK) + tapir_user.share_owner.refresh_from_db() print(tapir_user.share_owner.deleted_at) self.assertIsNotNone(tapir_user.share_owner.deleted_at) self.assertEqual(DeleteShareOwnerLogEntry.objects.count(), 1) From 52b1ffcd3b5ba65107e2dc27ba7f3ce9d6c2f1a9 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:49:21 +0100 Subject: [PATCH 30/56] use SoftDeleteMixin to get ShareOwner use Soft-Delete --- ...0058_alter_shareowner_managers_and_more.py | 22 ++++++++++++ tapir/coop/models.py | 34 +++++++++++++------ .../coop/tests/test_shareowner_softdelete.py | 10 +++--- tapir/coop/views/shareowner.py | 3 +- 4 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py diff --git a/tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py b/tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py new file mode 100644 index 000000000..52b031541 --- /dev/null +++ b/tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.12 on 2025-11-21 14:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("coop", "0057_deleteshareownerlogentry"), + ] + + operations = [ + migrations.AlterModelManagers( + name="shareowner", + managers=[], + ), + migrations.AlterField( + model_name="shareowner", + name="deleted_at", + field=models.DateTimeField(blank=True, default=None, null=True), + ), + ] diff --git a/tapir/coop/models.py b/tapir/coop/models.py index b0f481eec..0b29fed36 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -118,7 +118,28 @@ def get_queryset(self): return super().get_queryset().filter(deleted_at__isnull=True) -class ShareOwner(models.Model): +class SoftDeleteMixin(models.Model): + deleted_at = models.DateTimeField(null=True, blank=True, default=None) + + class Meta: + abstract = True + + objects = NonDeleted() + everything = models.Manager() + + def delete(self, using=None, keep_parents=False): + self.deleted_at = timezone.now() + self.save(update_fields=["deleted_at"]) + + def hard_delete(self, using=None, keep_parents=False): + super().delete(using=using, keep_parents=keep_parents) + + def restore(self): + self.deleted_at = None + self.save(update_fields=["deleted_at"]) + + +class ShareOwner(SoftDeleteMixin, models.Model): """ShareOwner represents a share_owner of a ShareOwnership. Usually, this is just a proxy for the associated user. However, it may also be used to @@ -182,19 +203,10 @@ class Meta: _("Is willing to gift a share"), null=True, blank=True ) create_account_reminder_email_sent = models.BooleanField(default=False) - deleted_at = models.DateTimeField(null=True, blank=True) # Soft-Delete - everything = models.Manager() + # overwrite from Mixin: objects = NonDeleted.from_queryset(ShareOwnerQuerySet)() - def soft_delete(self): - self.deleted_at = timezone.now() - self.save() - - def restore(self): - self.deleted_at = None - self.save() - def blank_info_fields(self): """Used after a ShareOwner is linked to a user, which is used as the source for user info instead.""" self.first_name = "" diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index 05c049198..ea22bf634 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -13,7 +13,7 @@ def test_softDelete_DeletedAt_isNotNone(self): share_owner = ShareOwnerFactory.create() self.assertIsNone(share_owner.deleted_at) - share_owner.soft_delete() + share_owner.delete() share_owner.refresh_from_db() @@ -23,7 +23,7 @@ def test_softDelete_DeletedAt_isNotNone(self): def test_softDelete_restoreDeleted_isNone(self): share_owner = ShareOwnerFactory.create() - share_owner.soft_delete() + share_owner.delete() share_owner.refresh_from_db() self.assertIsNotNone(share_owner.deleted_at) @@ -36,7 +36,7 @@ def test_softDelete_nonDeletedManager_containsNotDeletedShareowners(self): active_owner = ShareOwnerFactory.create() soft_deleted_owner = ShareOwnerFactory.create() - soft_deleted_owner.soft_delete() + soft_deleted_owner.delete() non_deleted_owners = ShareOwner.objects.all() self.assertIn(active_owner, non_deleted_owners) @@ -46,7 +46,7 @@ def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): owner1 = ShareOwnerFactory.create() owner2 = ShareOwnerFactory.create() - owner2.soft_delete() + owner2.delete() all_owners = ShareOwner.everything.all() @@ -57,7 +57,7 @@ def test_hardDelete_shareOwner_isfullyDeleted(self): share_owner = ShareOwnerFactory.create() all_users = ShareOwner.everything.all() self.assertIn(share_owner, all_users) - share_owner.delete() + share_owner.hard_delete() all_users = ShareOwner.everything.all() self.assertNotIn(share_owner, all_users) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 64f9b8ae4..237a56560 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -300,7 +300,8 @@ def form_valid(self, form): share_owner = self.get_object() if self.request.user == share_owner.user: raise PermissionDenied("You cannot delete your own account.") - self.get_object().soft_delete() + + self.get_object().delete() DeleteShareOwnerLogEntry().populate( share_owner=share_owner, actor=self.request.user, From ddd45471c03b5fe5f48bf5f3a96422ecd28fb876 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:50:51 +0100 Subject: [PATCH 31/56] translation --- .../locale/de/LC_MESSAGES/django.po | 162 ++++++++++-------- 1 file changed, 89 insertions(+), 73 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index 9b22df79d..195545625 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-18 21:06+0200\n" +"POT-Creation-Date: 2025-11-21 15:49+0100\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -56,12 +56,12 @@ msgstr "Wichtige E-Mails" msgid "Displayed name" msgstr "Angezeigter Name" -#: accounts/models.py:70 coop/models.py:157 coop/models.py:463 +#: accounts/models.py:70 coop/models.py:178 coop/models.py:475 msgid "Pronouns" msgstr "Pronomen" #: accounts/models.py:71 accounts/templates/accounts/user_detail.html:71 -#: coop/models.py:159 coop/models.py:465 +#: coop/models.py:180 coop/models.py:477 #: coop/templates/coop/draftuser_detail.html:71 #: coop/templates/coop/draftuser_detail.html:142 #: coop/templates/coop/shareowner_detail.html:51 @@ -69,29 +69,29 @@ msgid "Phone number" msgstr "Telefonnummer" #: accounts/models.py:72 accounts/templates/accounts/user_detail.html:81 -#: coop/models.py:160 coop/models.py:466 +#: coop/models.py:181 coop/models.py:478 #: coop/templates/coop/draftuser_detail.html:146 #: coop/templates/coop/shareowner_detail.html:55 msgid "Birthdate" msgstr "Geburtsdatum" -#: accounts/models.py:73 coop/models.py:161 coop/models.py:467 +#: accounts/models.py:73 coop/models.py:182 coop/models.py:479 msgid "Street and house number" msgstr "Straße und Hausnummer" -#: accounts/models.py:74 coop/models.py:162 coop/models.py:468 +#: accounts/models.py:74 coop/models.py:183 coop/models.py:480 msgid "Extra address line" msgstr "Adresszusatz" -#: accounts/models.py:75 coop/models.py:163 coop/models.py:469 +#: accounts/models.py:75 coop/models.py:184 coop/models.py:481 msgid "Postcode" msgstr "Postleitzahl" -#: accounts/models.py:76 coop/models.py:164 coop/models.py:470 +#: accounts/models.py:76 coop/models.py:185 coop/models.py:482 msgid "City" msgstr "Ort" -#: accounts/models.py:77 coop/models.py:165 coop/models.py:471 +#: accounts/models.py:77 coop/models.py:186 coop/models.py:483 msgid "Country" msgstr "Land" @@ -120,7 +120,7 @@ msgid "Allow purchase tracking" msgstr "Erlaube Aufzeichnen deiner Einkäufe" #: accounts/models.py:90 accounts/templates/accounts/user_detail.html:101 -#: coop/models.py:168 coop/models.py:474 +#: coop/models.py:189 coop/models.py:486 #: coop/templates/coop/shareowner_detail.html:75 msgid "Preferred Language" msgstr "Bevorzugte Sprache" @@ -299,7 +299,7 @@ msgstr "Aktivieren" msgid "You can only look at your own barcode unless you have admin rights" msgstr "Du kannst nur deinen eigenen Strichcode sehen, es sei denn du hast Admin-Rechte" -#: accounts/templates/accounts/user_detail.html:20 coop/models.py:742 +#: accounts/templates/accounts/user_detail.html:20 coop/models.py:754 #: coop/templates/coop/draftuser_detail.html:86 #: coop/templates/coop/shareowner_detail.html:10 log/views.py:94 #: log/views.py:152 shifts/templates/shifts/shift_day_printable.html:55 @@ -523,8 +523,8 @@ msgstr "Schickt mir eine Anleitung!" msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters." msgstr "Der angegebene Name ist ungültig. Er darf nur Buchstaben, Zahlen und die Symbole ./-/_ enthalten." -#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:273 -#: coop/views/shareowner.py:278 +#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:274 +#: coop/views/shareowner.py:279 #, python-format msgid "Edit member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" @@ -641,7 +641,7 @@ msgstr "Anzahl zu erstellender Anteile" msgid "The end date must be later than the start date." msgstr "Das Enddatum muss später als das Start-Datum sein" -#: coop/forms.py:105 coop/models.py:480 +#: coop/forms.py:105 coop/models.py:492 msgid "Number of Shares" msgstr "Anzahl Anteile" @@ -726,172 +726,172 @@ msgstr "Absender und Empfänger der Übertragung der Anteile können nicht ident msgid "Cannot pay out, because shares have been gifted." msgstr "Die Anteile können nicht ausgezahlt werden, da sie verschenkt wurden" -#: coop/models.py:141 +#: coop/models.py:162 msgid "Is company" msgstr "Ist eine Firma" -#: coop/models.py:148 coop/models.py:454 +#: coop/models.py:169 coop/models.py:466 msgid "Administrative first name" msgstr "Amtlicher Vorname" -#: coop/models.py:150 coop/models.py:456 +#: coop/models.py:171 coop/models.py:468 msgid "Last name" msgstr "Nachname" -#: coop/models.py:152 coop/models.py:458 +#: coop/models.py:173 coop/models.py:470 msgid "Usage name" msgstr "Angezeigter Name" -#: coop/models.py:158 coop/models.py:464 +#: coop/models.py:179 coop/models.py:476 msgid "Email address" msgstr "E-Mail-Adresse" -#: coop/models.py:175 +#: coop/models.py:196 msgid "Is investing member" msgstr "Ist investierendes Mitglied" -#: coop/models.py:177 coop/models.py:494 +#: coop/models.py:198 coop/models.py:506 #: coop/templates/coop/draftuser_detail.html:176 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:57 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 msgid "Ratenzahlung" msgstr "Ratenzahlung" -#: coop/models.py:179 coop/models.py:487 +#: coop/models.py:200 coop/models.py:499 msgid "Attended Welcome Session" msgstr "An Willkommenstreffen teilgenommen" -#: coop/models.py:182 +#: coop/models.py:203 msgid "Is willing to gift a share" msgstr "Ist bereit Anteile zu verschenken" -#: coop/models.py:217 +#: coop/models.py:229 msgid "Cannot be a company and have a Tapir account" msgstr "Kann keine Firma sein und ein Tapir-Konto haben" -#: coop/models.py:233 +#: coop/models.py:245 msgid "User info should be stored in associated Tapir account" msgstr "Benutzer Infos sollen in dem Tapir Konto gespeichert warden" -#: coop/models.py:385 +#: coop/models.py:397 msgid "Not a member" msgstr "Kein Mitlied" -#: coop/models.py:386 coop/templates/coop/draftuser_detail.html:169 +#: coop/models.py:398 coop/templates/coop/draftuser_detail.html:169 msgid "Investing" msgstr "Investierend" -#: coop/models.py:387 coop/templates/coop/draftuser_detail.html:171 +#: coop/models.py:399 coop/templates/coop/draftuser_detail.html:171 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:112 #: coop/views/statistics.py:135 msgid "Active" msgstr "Aktiv" -#: coop/models.py:388 +#: coop/models.py:400 msgid "Paused" msgstr "Pausiert" -#: coop/models.py:483 +#: coop/models.py:495 msgid "Investing member" msgstr "Investierendes Mitglied" -#: coop/models.py:490 +#: coop/models.py:502 msgid "Signed Beteiligungserklärung" msgstr "Beteiligungserklärung unterschrieben" -#: coop/models.py:492 +#: coop/models.py:504 msgid "Paid Entrance Fee" msgstr "Eintrittsgeld bezahlt" -#: coop/models.py:549 +#: coop/models.py:561 msgid "Email address must be set." msgstr "Email-Adresse muss gesetzt sein." -#: coop/models.py:551 +#: coop/models.py:563 msgid "First name must be set." msgstr "Vorname muss gesetzt sein." -#: coop/models.py:553 +#: coop/models.py:565 msgid "Last name must be set." msgstr "Nachname muss gesetzt sein." -#: coop/models.py:557 +#: coop/models.py:569 msgid "Membership agreement must be signed." msgstr "Mitgliedsantrag muss unterschrieben sein." -#: coop/models.py:559 +#: coop/models.py:571 msgid "Amount of requested shares must be positive." msgstr "Die Anzahl der erwünschten Anteile muss positiv sein." -#: coop/models.py:561 +#: coop/models.py:573 msgid "Member already created." msgstr "Mitglied schon vorhanden." -#: coop/models.py:588 +#: coop/models.py:600 msgid "Paying member" msgstr "Zahlendes Mitglied" -#: coop/models.py:596 +#: coop/models.py:608 msgid "Credited member" msgstr "Empfangendes Mitglied" -#: coop/models.py:603 +#: coop/models.py:615 msgid "Amount" msgstr "Betrag" -#: coop/models.py:609 +#: coop/models.py:621 msgid "Payment date" msgstr "Zahldatum" -#: coop/models.py:612 +#: coop/models.py:624 msgid "Creation date" msgstr "Erstellungsdatum" -#: coop/models.py:617 +#: coop/models.py:629 msgid "Created by" msgstr "Erstellt durch" -#: coop/models.py:792 +#: coop/models.py:804 msgid "The cooperative buys the shares back from the member" msgstr "Die Kooperative kauft die Anteile des Mitglieds zurück." -#: coop/models.py:795 +#: coop/models.py:807 #, fuzzy #| msgid "Number of shares to create" msgid "The member gifts the shares to the cooperative" msgstr "Anzahl zu erstellender Anteile" -#: coop/models.py:797 +#: coop/models.py:809 msgid "The shares get transferred to another member" msgstr "Die Anteile werden an ein anderes Mitglied übertragen" -#: coop/models.py:800 +#: coop/models.py:812 msgid "Financial reasons" msgstr "Finanzielle Gründe" -#: coop/models.py:801 +#: coop/models.py:813 msgid "Health reasons" msgstr "Gesundheit" -#: coop/models.py:802 +#: coop/models.py:814 msgid "Distance" msgstr "Entfernung" -#: coop/models.py:803 +#: coop/models.py:815 msgid "Strategic orientation of SuperCoop" msgstr "Strategische Ausrichtung von Supercoop" -#: coop/models.py:804 +#: coop/models.py:816 msgid "Other" msgstr "Andere" -#: coop/models.py:809 +#: coop/models.py:821 #, fuzzy #| msgid "Edit shareowner" msgid "Shareowner" msgstr "Mitglied bearbeiten" -#: coop/models.py:828 +#: coop/models.py:840 msgid "Leave this empty if the resignation type is not a transfer to another member" msgstr "Lass das Feld leer, wenn es sich nicht um eine Übertragung auf ein anderes Mitglied handelt" @@ -929,8 +929,8 @@ msgid "Confirm delete" msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 -#: coop/templates/coop/confirm_delete_share_ownership.html:25 -#: coop/templates/coop/shareowner_confirm_delete.html:26 +#: coop/templates/coop/confirm_delete_share_ownership.html:24 +#: coop/templates/coop/shareowner_confirm_delete.html:24 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:30 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:136 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 @@ -1000,7 +1000,7 @@ msgstr "Liste alle E-Mails" #: coop/templates/coop/draftuser_detail.html:69 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 -#: coop/views/shareowner.py:621 +#: coop/views/shareowner.py:628 #: shifts/templates/shifts/user_shifts_overview_tag.html:21 msgid "Status" msgstr "Status" @@ -2014,6 +2014,14 @@ msgid "" " Reactivated resigned member\n" msgstr "Mitglied bearbeiten:" +#: coop/templates/coop/log/delete_share_owner_log_entry.html:2 +#, fuzzy +#| msgid "Members on alert" +msgid "" +"\n" +"Member has been deleted.\n" +msgstr "Mitglieder mit Verwarnung" + #: coop/templates/coop/log/update_incoming_payment_log_entry.html:2 #, fuzzy #| msgid "Create applicant" @@ -2288,7 +2296,15 @@ msgstr "" msgid "Confirm Delete" msgstr "Löschen bestätigen" -#: coop/templates/coop/shareowner_confirm_delete.html:29 +#: coop/templates/coop/shareowner_confirm_delete.html:16 +msgid "Are you sure you want to delete this member?" +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:18 +msgid "Please note that deleting this member will permanently remove their account and all associated data such as owned shares and payments. The deleted user will no longer be part of the cooperative. However, all shift records and logs will remain intact." +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:27 #: shifts/templates/shifts/user_shifts_overview_tag.html:172 msgid "Cancel" msgstr "Abmelden" @@ -2552,68 +2568,68 @@ msgstr "ohne Endddatum" msgid "Cancel membership of %(name)s" msgstr "Mitglied bearbeiten: %(name)s" -#: coop/views/shareowner.py:134 coop/views/shareowner.py:139 +#: coop/views/shareowner.py:135 coop/views/shareowner.py:140 #, python-format msgid "Edit share: %(name)s" msgstr "Anteil bearbeiten: %(name)s" -#: coop/views/shareowner.py:159 coop/views/shareowner.py:164 +#: coop/views/shareowner.py:160 coop/views/shareowner.py:165 #, python-format msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:400 +#: coop/views/shareowner.py:407 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:622 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:629 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:627 +#: coop/views/shareowner.py:634 #: shifts/templates/shifts/user_shifts_overview_tag.html:74 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:635 +#: coop/views/shareowner.py:642 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:643 +#: coop/views/shareowner.py:650 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:651 +#: coop/views/shareowner.py:658 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:659 +#: coop/views/shareowner.py:666 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:666 +#: coop/views/shareowner.py:673 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:669 +#: coop/views/shareowner.py:676 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:672 +#: coop/views/shareowner.py:679 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:676 +#: coop/views/shareowner.py:683 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:681 +#: coop/views/shareowner.py:688 #, fuzzy #| msgid "Shift missed" msgid "Shift Name" msgstr "Schicht versäumt" -#: coop/views/shareowner.py:941 +#: coop/views/shareowner.py:948 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" From bd860cd63b0746b93de282041d898f0ba9cea17e Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 15:56:19 +0100 Subject: [PATCH 32/56] delete old migrations --- ...areowner_managers_shareowner_deleted_at.py | 28 ------------------- ...areownerlogentry_shareowner_deleted_at.py} | 12 ++++++-- ...0058_alter_shareowner_managers_and_more.py | 22 --------------- 3 files changed, 10 insertions(+), 52 deletions(-) delete mode 100644 tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py rename tapir/coop/migrations/{0057_deleteshareownerlogentry.py => 0056_deleteshareownerlogentry_shareowner_deleted_at.py} (73%) delete mode 100644 tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py diff --git a/tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py b/tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py deleted file mode 100644 index cc8239ffe..000000000 --- a/tapir/coop/migrations/0056_alter_shareowner_managers_shareowner_deleted_at.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.12 on 2025-10-05 08:15 - -import django.db.models.manager -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ( - "coop", - "0055_membershippauseupdatedlogentry_coop_member_old_val_6e6554_gin_and_more", - ), - ] - - operations = [ - migrations.AlterModelManagers( - name="shareowner", - managers=[ - ("everything", django.db.models.manager.Manager()), - ], - ), - migrations.AddField( - model_name="shareowner", - name="deleted_at", - field=models.DateTimeField(blank=True, null=True), - ), - ] diff --git a/tapir/coop/migrations/0057_deleteshareownerlogentry.py b/tapir/coop/migrations/0056_deleteshareownerlogentry_shareowner_deleted_at.py similarity index 73% rename from tapir/coop/migrations/0057_deleteshareownerlogentry.py rename to tapir/coop/migrations/0056_deleteshareownerlogentry_shareowner_deleted_at.py index 90044b160..40abc3163 100644 --- a/tapir/coop/migrations/0057_deleteshareownerlogentry.py +++ b/tapir/coop/migrations/0056_deleteshareownerlogentry_shareowner_deleted_at.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.12 on 2025-11-16 14:01 +# Generated by Django 5.1.12 on 2025-11-21 14:55 import django.contrib.postgres.fields.hstore import django.db.models.deletion @@ -8,7 +8,10 @@ class Migration(migrations.Migration): dependencies = [ - ("coop", "0056_alter_shareowner_managers_shareowner_deleted_at"), + ( + "coop", + "0055_membershippauseupdatedlogentry_coop_member_old_val_6e6554_gin_and_more", + ), ("log", "0008_logentry_log_logentr_user_id_c4ca60_idx_and_more"), ] @@ -34,4 +37,9 @@ class Migration(migrations.Migration): }, bases=("log.logentry",), ), + migrations.AddField( + model_name="shareowner", + name="deleted_at", + field=models.DateTimeField(blank=True, default=None, null=True), + ), ] diff --git a/tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py b/tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py deleted file mode 100644 index 52b031541..000000000 --- a/tapir/coop/migrations/0058_alter_shareowner_managers_and_more.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.1.12 on 2025-11-21 14:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("coop", "0057_deleteshareownerlogentry"), - ] - - operations = [ - migrations.AlterModelManagers( - name="shareowner", - managers=[], - ), - migrations.AlterField( - model_name="shareowner", - name="deleted_at", - field=models.DateTimeField(blank=True, default=None, null=True), - ), - ] From a6349a173c363f8e1d6f758aa18d10896f4211f6 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:10:56 +0100 Subject: [PATCH 33/56] tests users cannot delete itself --- .../coop/tests/test_shareowner_delete_view.py | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py index 8559627e8..3d5464cca 100644 --- a/tapir/coop/tests/test_shareowner_delete_view.py +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -4,7 +4,7 @@ from tapir import settings from tapir.accounts.tests.factories.factories import TapirUserFactory -from tapir.coop.models import DeleteShareOwnerLogEntry +from tapir.coop.models import DeleteShareOwnerLogEntry, ShareOwner from tapir.utils.tests_utils import ( PermissionTestMixin, FeatureFlagTestMixin, @@ -31,7 +31,7 @@ def do_request(self): follow=True, ) - def test_shareOwnerGetsDeleted_deletedAt_hasDate(self): + def test_ShareOwnerDeleteView_shareOwnerdeletedAt_hasDate(self): self.login_as_vorstand() tapir_user = TapirUserFactory() response = self.client.post( @@ -40,6 +40,27 @@ def test_shareOwnerGetsDeleted_deletedAt_hasDate(self): ) self.assertStatusCode(response, HTTPStatus.OK) tapir_user.share_owner.refresh_from_db() - print(tapir_user.share_owner.deleted_at) self.assertIsNotNone(tapir_user.share_owner.deleted_at) self.assertEqual(DeleteShareOwnerLogEntry.objects.count(), 1) + + def test_ShareOwnerDeleteView_normalShareOwner_cannotDeleteItself(self): + tapir_user = self.login_as_normal_user() + response = self.client.post( + reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), + follow=True, + ) + self.assertStatusCode(response, HTTPStatus.FORBIDDEN) + tapir_user.share_owner.refresh_from_db() + self.assertIsNone(tapir_user.share_owner.deleted_at) + self.assertEqual(DeleteShareOwnerLogEntry.objects.count(), 0) + + def test_ShareOwnerDeleteView_vorstandShareOwner_cannotDeleteItself(self): + tapir_user = self.login_as_vorstand() + response = self.client.post( + reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), + follow=True, + ) + self.assertStatusCode(response, HTTPStatus.FORBIDDEN) + tapir_user.share_owner.refresh_from_db() + self.assertIsNone(tapir_user.share_owner.deleted_at) + self.assertEqual(DeleteShareOwnerLogEntry.objects.count(), 0) From 79a63a0293394e1116d6e5572e333a711eca7e58 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 16:16:21 +0100 Subject: [PATCH 34/56] translation --- .../locale/de/LC_MESSAGES/django.po | 232 ++++++++++-------- 1 file changed, 129 insertions(+), 103 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index adb60d90c..31bc4bb7b 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-21 12:31+0100\n" +"POT-Creation-Date: 2025-11-21 16:15+0100\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -56,12 +56,12 @@ msgstr "Wichtige E-Mails" msgid "Displayed name" msgstr "Angezeigter Name" -#: accounts/models.py:70 coop/models.py:70 coop/models.py:445 +#: accounts/models.py:70 coop/models.py:178 coop/models.py:475 msgid "Pronouns" msgstr "Pronomen" #: accounts/models.py:71 accounts/templates/accounts/user_detail.html:71 -#: coop/models.py:72 coop/models.py:447 +#: coop/models.py:180 coop/models.py:477 #: coop/templates/coop/draftuser_detail.html:71 #: coop/templates/coop/draftuser_detail.html:142 #: coop/templates/coop/shareowner_detail.html:51 @@ -69,29 +69,29 @@ msgid "Phone number" msgstr "Telefonnummer" #: accounts/models.py:72 accounts/templates/accounts/user_detail.html:81 -#: coop/models.py:73 coop/models.py:448 +#: coop/models.py:181 coop/models.py:478 #: coop/templates/coop/draftuser_detail.html:146 #: coop/templates/coop/shareowner_detail.html:55 msgid "Birthdate" msgstr "Geburtsdatum" -#: accounts/models.py:73 coop/models.py:74 coop/models.py:449 +#: accounts/models.py:73 coop/models.py:182 coop/models.py:479 msgid "Street and house number" msgstr "Straße und Hausnummer" -#: accounts/models.py:74 coop/models.py:75 coop/models.py:450 +#: accounts/models.py:74 coop/models.py:183 coop/models.py:480 msgid "Extra address line" msgstr "Adresszusatz" -#: accounts/models.py:75 coop/models.py:76 coop/models.py:451 +#: accounts/models.py:75 coop/models.py:184 coop/models.py:481 msgid "Postcode" msgstr "Postleitzahl" -#: accounts/models.py:76 coop/models.py:77 coop/models.py:452 +#: accounts/models.py:76 coop/models.py:185 coop/models.py:482 msgid "City" msgstr "Ort" -#: accounts/models.py:77 coop/models.py:78 coop/models.py:453 +#: accounts/models.py:77 coop/models.py:186 coop/models.py:483 msgid "Country" msgstr "Land" @@ -120,7 +120,7 @@ msgid "Allow purchase tracking" msgstr "Erlaube Aufzeichnen deiner Einkäufe" #: accounts/models.py:90 accounts/templates/accounts/user_detail.html:101 -#: coop/models.py:81 coop/models.py:456 +#: coop/models.py:189 coop/models.py:486 #: coop/templates/coop/shareowner_detail.html:75 msgid "Preferred Language" msgstr "Bevorzugte Sprache" @@ -279,8 +279,8 @@ msgid "I agree that my membership card will be scanned at the checkout when I ma msgstr "Ich bin damit einverstanden, dass meine Mitgliedskarte beim Einkauf an der Kasse gescannt und somit mein Einkauf erfasst und gespeichert wird:" #: accounts/templates/accounts/purchase_tracking_card.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:168 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:172 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:177 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 msgid "Yes,No" msgstr "Ja, Nein" @@ -299,7 +299,7 @@ msgstr "Aktivieren" msgid "You can only look at your own barcode unless you have admin rights" msgstr "Du kannst nur deinen eigenen Strichcode sehen, es sei denn du hast Admin-Rechte" -#: accounts/templates/accounts/user_detail.html:20 coop/models.py:724 +#: accounts/templates/accounts/user_detail.html:20 coop/models.py:754 #: coop/templates/coop/draftuser_detail.html:86 #: coop/templates/coop/shareowner_detail.html:10 log/views.py:94 #: log/views.py:152 shifts/templates/shifts/shift_day_printable_v0.html:55 @@ -330,9 +330,9 @@ msgstr "Benutzername bearbeiten" #: coop/templates/coop/draftuser_detail.html:238 #: coop/templates/coop/membershipresignation_detail.html:107 #: coop/templates/coop/shareowner_detail.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:22 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:23 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:99 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:122 #: core/templates/core/featureflag_list.html:30 #: shifts/templates/shifts/shift_detail.html:60 #: shifts/templates/shifts/shift_template_detail.html:25 @@ -523,8 +523,8 @@ msgstr "Schickt mir eine Anleitung!" msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters." msgstr "Der angegebene Name ist ungültig. Er darf nur Buchstaben, Zahlen und die Symbole ./-/_ enthalten." -#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:275 -#: coop/views/shareowner.py:280 +#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:280 +#: coop/views/shareowner.py:285 #, python-format msgid "Edit member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" @@ -641,7 +641,7 @@ msgstr "Anzahl zu erstellender Anteile" msgid "The end date must be later than the start date." msgstr "Das Enddatum muss später als das Start-Datum sein" -#: coop/forms.py:105 coop/models.py:462 +#: coop/forms.py:105 coop/models.py:492 msgid "Number of Shares" msgstr "Anzahl Anteile" @@ -726,172 +726,172 @@ msgstr "Absender und Empfänger der Übertragung der Anteile können nicht ident msgid "Cannot pay out, because shares have been gifted." msgstr "Die Anteile können nicht ausgezahlt werden, da sie verschenkt wurden" -#: coop/models.py:54 +#: coop/models.py:162 msgid "Is company" msgstr "Ist eine Firma" -#: coop/models.py:61 coop/models.py:436 +#: coop/models.py:169 coop/models.py:466 msgid "Administrative first name" msgstr "Amtlicher Vorname" -#: coop/models.py:63 coop/models.py:438 +#: coop/models.py:171 coop/models.py:468 msgid "Last name" msgstr "Nachname" -#: coop/models.py:65 coop/models.py:440 +#: coop/models.py:173 coop/models.py:470 msgid "Usage name" msgstr "Angezeigter Name" -#: coop/models.py:71 coop/models.py:446 +#: coop/models.py:179 coop/models.py:476 msgid "Email address" msgstr "E-Mail-Adresse" -#: coop/models.py:88 +#: coop/models.py:196 msgid "Is investing member" msgstr "Ist investierendes Mitglied" -#: coop/models.py:90 coop/models.py:476 +#: coop/models.py:198 coop/models.py:506 #: coop/templates/coop/draftuser_detail.html:176 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:48 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:167 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:57 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 msgid "Ratenzahlung" msgstr "Ratenzahlung" -#: coop/models.py:92 coop/models.py:469 +#: coop/models.py:200 coop/models.py:499 msgid "Attended Welcome Session" msgstr "An Willkommenstreffen teilgenommen" -#: coop/models.py:95 +#: coop/models.py:203 msgid "Is willing to gift a share" msgstr "Ist bereit Anteile zu verschenken" -#: coop/models.py:199 +#: coop/models.py:229 msgid "Cannot be a company and have a Tapir account" msgstr "Kann keine Firma sein und ein Tapir-Konto haben" -#: coop/models.py:215 +#: coop/models.py:245 msgid "User info should be stored in associated Tapir account" msgstr "Benutzer Infos sollen in dem Tapir Konto gespeichert warden" -#: coop/models.py:367 +#: coop/models.py:397 msgid "Not a member" msgstr "Kein Mitlied" -#: coop/models.py:368 coop/templates/coop/draftuser_detail.html:169 +#: coop/models.py:398 coop/templates/coop/draftuser_detail.html:169 msgid "Investing" msgstr "Investierend" -#: coop/models.py:369 coop/templates/coop/draftuser_detail.html:171 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:103 +#: coop/models.py:399 coop/templates/coop/draftuser_detail.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:112 #: coop/views/statistics.py:135 msgid "Active" msgstr "Aktiv" -#: coop/models.py:370 +#: coop/models.py:400 msgid "Paused" msgstr "Pausiert" -#: coop/models.py:465 +#: coop/models.py:495 msgid "Investing member" msgstr "Investierendes Mitglied" -#: coop/models.py:472 +#: coop/models.py:502 msgid "Signed Beteiligungserklärung" msgstr "Beteiligungserklärung unterschrieben" -#: coop/models.py:474 +#: coop/models.py:504 msgid "Paid Entrance Fee" msgstr "Eintrittsgeld bezahlt" -#: coop/models.py:531 +#: coop/models.py:561 msgid "Email address must be set." msgstr "Email-Adresse muss gesetzt sein." -#: coop/models.py:533 +#: coop/models.py:563 msgid "First name must be set." msgstr "Vorname muss gesetzt sein." -#: coop/models.py:535 +#: coop/models.py:565 msgid "Last name must be set." msgstr "Nachname muss gesetzt sein." -#: coop/models.py:539 +#: coop/models.py:569 msgid "Membership agreement must be signed." msgstr "Mitgliedsantrag muss unterschrieben sein." -#: coop/models.py:541 +#: coop/models.py:571 msgid "Amount of requested shares must be positive." msgstr "Die Anzahl der erwünschten Anteile muss positiv sein." -#: coop/models.py:543 +#: coop/models.py:573 msgid "Member already created." msgstr "Mitglied schon vorhanden." -#: coop/models.py:570 +#: coop/models.py:600 msgid "Paying member" msgstr "Zahlendes Mitglied" -#: coop/models.py:578 +#: coop/models.py:608 msgid "Credited member" msgstr "Empfangendes Mitglied" -#: coop/models.py:585 +#: coop/models.py:615 msgid "Amount" msgstr "Betrag" -#: coop/models.py:591 +#: coop/models.py:621 msgid "Payment date" msgstr "Zahldatum" -#: coop/models.py:594 +#: coop/models.py:624 msgid "Creation date" msgstr "Erstellungsdatum" -#: coop/models.py:599 +#: coop/models.py:629 msgid "Created by" msgstr "Erstellt durch" -#: coop/models.py:774 +#: coop/models.py:804 msgid "The cooperative buys the shares back from the member" msgstr "Die Kooperative kauft die Anteile des Mitglieds zurück." -#: coop/models.py:777 +#: coop/models.py:807 #, fuzzy #| msgid "Number of shares to create" msgid "The member gifts the shares to the cooperative" msgstr "Anzahl zu erstellender Anteile" -#: coop/models.py:779 +#: coop/models.py:809 msgid "The shares get transferred to another member" msgstr "Die Anteile werden an ein anderes Mitglied übertragen" -#: coop/models.py:782 +#: coop/models.py:812 msgid "Financial reasons" msgstr "Finanzielle Gründe" -#: coop/models.py:783 +#: coop/models.py:813 msgid "Health reasons" msgstr "Gesundheit" -#: coop/models.py:784 +#: coop/models.py:814 msgid "Distance" msgstr "Entfernung" -#: coop/models.py:785 +#: coop/models.py:815 msgid "Strategic orientation of SuperCoop" msgstr "Strategische Ausrichtung von Supercoop" -#: coop/models.py:786 +#: coop/models.py:816 msgid "Other" msgstr "Andere" -#: coop/models.py:791 +#: coop/models.py:821 #, fuzzy #| msgid "Edit shareowner" msgid "Shareowner" msgstr "Mitglied bearbeiten" -#: coop/models.py:810 +#: coop/models.py:840 msgid "Leave this empty if the resignation type is not a transfer to another member" msgstr "Lass das Feld leer, wenn es sich nicht um eine Übertragung auf ein anderes Mitglied handelt" @@ -930,7 +930,9 @@ msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 #: coop/templates/coop/confirm_delete_share_ownership.html:24 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:127 +#: coop/templates/coop/shareowner_confirm_delete.html:24 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:30 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:136 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 #: shifts/templates/shifts/shift_confirm_delete.html:30 msgid "Delete" @@ -996,9 +998,9 @@ msgid "List of similar members" msgstr "Liste alle E-Mails" #: coop/templates/coop/draftuser_detail.html:69 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:31 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:88 -#: coop/views/shareowner.py:601 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 +#: coop/views/shareowner.py:634 #: shifts/templates/shifts/user_shifts_overview_tag.html:21 msgid "Status" msgstr "Status" @@ -1021,7 +1023,7 @@ msgid "Shares requested" msgstr "Angeforderte Anteile" #: coop/templates/coop/draftuser_detail.html:190 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:53 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:62 #: shifts/models.py:44 msgid "Welcome Session" msgstr "Willkommenstreffen" @@ -1975,7 +1977,7 @@ msgid "General Tapir Accounts" msgstr "Tapir-Konto erstellen" #: coop/templates/coop/incoming_payment_list.html:9 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:160 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:169 msgid "Payments" msgstr "Zahlungen" @@ -2012,6 +2014,14 @@ msgid "" " Reactivated resigned member\n" msgstr "Mitglied bearbeiten:" +#: coop/templates/coop/log/delete_share_owner_log_entry.html:2 +#, fuzzy +#| msgid "Members on alert" +msgid "" +"\n" +"Member has been deleted.\n" +msgstr "Mitglieder mit Verwarnung" + #: coop/templates/coop/log/update_incoming_payment_log_entry.html:2 #, fuzzy #| msgid "Create applicant" @@ -2283,6 +2293,26 @@ msgstr "" msgid "Organisation logo" msgstr "" +#: coop/templates/coop/shareowner_confirm_delete.html:7 +#: coop/templates/coop/shareowner_confirm_delete.html:13 +#, fuzzy +#| msgid "Confirm delete" +msgid "Confirm Delete" +msgstr "Löschen bestätigen" + +#: coop/templates/coop/shareowner_confirm_delete.html:16 +msgid "Are you sure you want to delete this member?" +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:18 +msgid "Please note that deleting this member will permanently remove their account and all associated data such as owned shares and payments. The deleted user will no longer be part of the cooperative. However, all shift records and logs will remain intact." +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:27 +#: shifts/templates/shifts/user_shifts_overview_tag.html:172 +msgid "Cancel" +msgstr "Abmelden" + #: coop/templates/coop/shareowner_detail.html:22 msgid "Go to user page" msgstr "Zur Nutzer*innen-Seite" @@ -2365,11 +2395,11 @@ msgstr "" " Mitglied #%(coop_share_owner_id)s\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:17 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:18 msgid "Membership confirmation" msgstr "Mitgliedsbestätigung" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:35 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:44 #, python-format msgid "" "\n" @@ -2377,7 +2407,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:49 #, python-format msgid "" "\n" @@ -2385,7 +2415,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 #: shifts/models.py:955 shifts/templates/shifts/shift_day_printable.html:207 #: shifts/templates/shifts/shift_day_printable.html:269 #: shifts/templates/shifts/shift_day_printable_v0.html:56 @@ -2394,36 +2424,36 @@ msgstr "" msgid "Attended" msgstr "Teilgenommen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:67 #: shifts/models.py:954 msgid "Pending" msgstr "Ausstehend" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 msgid "Mark Attended" msgstr "Als teilgenommen markieren" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:83 msgid "Owned shares" msgstr "Anteile" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:82 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:91 msgid "List of shares owned by this member" msgstr "Liste der Anteile die dieses Mitglied gehören" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:86 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:95 msgid "Starts at" msgstr "Anfangsdatum" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:87 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:96 msgid "Ends at" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:105 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:114 msgid "Sold or future" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:130 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:139 msgid "" "\n" " Only use this to correct mistakes, i.e. if the share was\n" @@ -2437,19 +2467,19 @@ msgstr "" "Bitte nur verwenden, wenn ein tatsächlicher Fehler vorliegt, z.B. ein Anteil eingetragen wurde, der nie gekauft wurde. Wenn die Person ihren Anteil einfach an den Coop zurückverkauft hat, markiere den Anteil bitte als 'verkauft'.\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:153 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:162 msgid "Add Shares" msgstr "Anteile hinzufügen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:180 msgid "Willing to gift a share" msgstr "Bereit Anteile zu schenken" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:190 msgid "Send membership confirmation email" msgstr "Mitgliedsbestätigung per Mail senden" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:187 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:196 msgid "User is not a cooperative member." msgstr "Benutzer*in ist kein Genossenschaftsmitglied." @@ -2544,68 +2574,68 @@ msgstr "ohne Endddatum" msgid "Cancel membership of %(name)s" msgstr "Mitglied bearbeiten: %(name)s" -#: coop/views/shareowner.py:136 coop/views/shareowner.py:141 +#: coop/views/shareowner.py:141 coop/views/shareowner.py:146 #, python-format msgid "Edit share: %(name)s" msgstr "Anteil bearbeiten: %(name)s" -#: coop/views/shareowner.py:161 coop/views/shareowner.py:166 +#: coop/views/shareowner.py:166 coop/views/shareowner.py:171 #, python-format msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:380 +#: coop/views/shareowner.py:413 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:602 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:635 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:607 +#: coop/views/shareowner.py:640 #: shifts/templates/shifts/user_shifts_overview_tag.html:74 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:615 +#: coop/views/shareowner.py:648 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:623 +#: coop/views/shareowner.py:656 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:631 +#: coop/views/shareowner.py:664 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:639 +#: coop/views/shareowner.py:672 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:646 +#: coop/views/shareowner.py:679 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:649 +#: coop/views/shareowner.py:682 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:652 +#: coop/views/shareowner.py:685 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:656 +#: coop/views/shareowner.py:689 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:661 +#: coop/views/shareowner.py:694 #, fuzzy #| msgid "Shift missed" msgid "Shift Name" msgstr "Schicht versäumt" -#: coop/views/shareowner.py:935 +#: coop/views/shareowner.py:954 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" @@ -4554,10 +4584,6 @@ msgstr "Solidaritäts-Schicht spenden" msgid "One of your banked shifts will be donated as a solidarity shift. Do you want to continue?" msgstr "Eine deiner gearbeiteten Schichten wird als Solidaritäts-Schicht gespendet. Möchtest du fortfahren?" -#: shifts/templates/shifts/user_shifts_overview_tag.html:172 -msgid "Cancel" -msgstr "Abmelden" - #: shifts/templates/shifts/user_shifts_overview_tag.html:175 msgid "Confirm" msgstr "" From 909f44570b6e6d5ba9da770ce64cbfb2333b649e Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:49:42 +0100 Subject: [PATCH 35/56] fix: return HTTPReponse --- tapir/coop/views/shareowner.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 8d1b05142..916844b88 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -302,11 +302,14 @@ def get_object(self, queryset=None): def get_success_url(self): return reverse("coop:shareowner_list") + def dispatch(self, request, *args, **kwargs): + obj = self.get_object() + if request.user == obj.user: + return HttpResponseForbidden("You cannot delete your own account.") + return super().dispatch(request, *args, **kwargs) + def form_valid(self, form): share_owner = self.get_object() - if self.request.user == share_owner.user: - raise PermissionDenied("You cannot delete your own account.") - self.get_object().delete() DeleteShareOwnerLogEntry().populate( share_owner=share_owner, From 1a5c3bc7e742adf911026bd09e00197156791cea Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:50:19 +0100 Subject: [PATCH 36/56] use @transaction.atomic --- tapir/coop/views/shareowner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 916844b88..c6420f995 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -308,6 +308,7 @@ def dispatch(self, request, *args, **kwargs): return HttpResponseForbidden("You cannot delete your own account.") return super().dispatch(request, *args, **kwargs) + @transaction.atomic def form_valid(self, form): share_owner = self.get_object() self.get_object().delete() From b7619273d7cd4153aae9d3e0dbff299b1e2c11a9 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:52:58 +0100 Subject: [PATCH 37/56] transl --- .../locale/de/LC_MESSAGES/django.po | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index 31bc4bb7b..16b86e9b3 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-21 16:15+0100\n" +"POT-Creation-Date: 2025-11-21 17:52+0100\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -1000,7 +1000,7 @@ msgstr "Liste alle E-Mails" #: coop/templates/coop/draftuser_detail.html:69 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 #: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 -#: coop/views/shareowner.py:634 +#: coop/views/shareowner.py:638 #: shifts/templates/shifts/user_shifts_overview_tag.html:21 msgid "Status" msgstr "Status" @@ -2584,58 +2584,58 @@ msgstr "Anteil bearbeiten: %(name)s" msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:413 +#: coop/views/shareowner.py:417 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:635 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:639 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:640 +#: coop/views/shareowner.py:644 #: shifts/templates/shifts/user_shifts_overview_tag.html:74 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:648 +#: coop/views/shareowner.py:652 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:656 +#: coop/views/shareowner.py:660 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:664 +#: coop/views/shareowner.py:668 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:672 +#: coop/views/shareowner.py:676 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:679 +#: coop/views/shareowner.py:683 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:682 +#: coop/views/shareowner.py:686 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:685 +#: coop/views/shareowner.py:689 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:689 +#: coop/views/shareowner.py:693 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:694 +#: coop/views/shareowner.py:698 #, fuzzy #| msgid "Shift missed" msgid "Shift Name" msgstr "Schicht versäumt" -#: coop/views/shareowner.py:954 +#: coop/views/shareowner.py:958 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" From 2b0416d694103dba379e66b1f6d0df34da4a2cea Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:02:02 +0100 Subject: [PATCH 38/56] Update tapir/coop/views/shareowner.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/views/shareowner.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index c6420f995..36b14585a 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -296,8 +296,6 @@ class ShareOwnerDeleteView( permission_required = PERMISSION_GROUP_MANAGE model = ShareOwner - def get_object(self, queryset=None): - return super().get_object(queryset) def get_success_url(self): return reverse("coop:shareowner_list") From 58af918afc19e4943103f38d5da28c6b6ca231ac Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:04:55 +0100 Subject: [PATCH 39/56] DeleteView automatically calls delete() so we don't need to call it here --- tapir/coop/views/shareowner.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 36b14585a..1f5148031 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -296,7 +296,6 @@ class ShareOwnerDeleteView( permission_required = PERMISSION_GROUP_MANAGE model = ShareOwner - def get_success_url(self): return reverse("coop:shareowner_list") @@ -309,7 +308,6 @@ def dispatch(self, request, *args, **kwargs): @transaction.atomic def form_valid(self, form): share_owner = self.get_object() - self.get_object().delete() DeleteShareOwnerLogEntry().populate( share_owner=share_owner, actor=self.request.user, From ba5826f074005cb6401579be9ec4d989f50a8763 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:22:06 +0100 Subject: [PATCH 40/56] move SoftDeleteMixin and Manager to coop/models.py --- tapir/coop/models.py | 27 +-------------------------- tapir/core/models.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/tapir/coop/models.py b/tapir/coop/models.py index 0b29fed36..f36726eb1 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -17,6 +17,7 @@ from tapir.coop.services.membership_pause_service import MembershipPauseService from tapir.coop.services.number_of_shares_service import NumberOfSharesService from tapir.core.config import help_text_displayed_name +from tapir.core.models import SoftDeleteMixin, NonDeleted from tapir.log.models import UpdateModelLogEntry, ModelLogEntry, LogEntry from tapir.utils.expection_utils import TapirException from tapir.utils.models import ( @@ -113,32 +114,6 @@ def with_status( raise TapirException(f"Invalid status : {status}") -class NonDeleted(models.Manager): - def get_queryset(self): - return super().get_queryset().filter(deleted_at__isnull=True) - - -class SoftDeleteMixin(models.Model): - deleted_at = models.DateTimeField(null=True, blank=True, default=None) - - class Meta: - abstract = True - - objects = NonDeleted() - everything = models.Manager() - - def delete(self, using=None, keep_parents=False): - self.deleted_at = timezone.now() - self.save(update_fields=["deleted_at"]) - - def hard_delete(self, using=None, keep_parents=False): - super().delete(using=using, keep_parents=keep_parents) - - def restore(self): - self.deleted_at = None - self.save(update_fields=["deleted_at"]) - - class ShareOwner(SoftDeleteMixin, models.Model): """ShareOwner represents a share_owner of a ShareOwnership. diff --git a/tapir/core/models.py b/tapir/core/models.py index e6214643c..117892d56 100644 --- a/tapir/core/models.py +++ b/tapir/core/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -27,3 +28,29 @@ def ensure_flag_exists(cls, flag_name): if cls.objects.filter(flag_name=flag_name).exists(): return cls.objects.create(flag_name=flag_name) + + +class NonDeleted(models.Manager): + def get_queryset(self): + return super().get_queryset().filter(deleted_at__isnull=True) + + +class SoftDeleteMixin(models.Model): + deleted_at = models.DateTimeField(null=True, blank=True, default=None) + + class Meta: + abstract = True + + objects = NonDeleted() + everything = models.Manager() + + def delete(self, using=None, keep_parents=False): + self.deleted_at = timezone.now() + self.save(update_fields=["deleted_at"]) + + def hard_delete(self, using=None, keep_parents=False): + super().delete(using=using, keep_parents=keep_parents) + + def restore(self): + self.deleted_at = None + self.save(update_fields=["deleted_at"]) From e3ec40dd0d7ef21a63191e5fad13881a94e05cbe Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:36:04 +0100 Subject: [PATCH 41/56] forward using to save --- tapir/core/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/core/models.py b/tapir/core/models.py index 117892d56..b67bf28ff 100644 --- a/tapir/core/models.py +++ b/tapir/core/models.py @@ -46,7 +46,7 @@ class Meta: def delete(self, using=None, keep_parents=False): self.deleted_at = timezone.now() - self.save(update_fields=["deleted_at"]) + self.save(update_fields=["deleted_at"], using=using) def hard_delete(self, using=None, keep_parents=False): super().delete(using=using, keep_parents=keep_parents) From f2039b1b9888b5e1ed538457715299d20cf55d90 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sun, 14 Dec 2025 18:32:53 +0100 Subject: [PATCH 42/56] translation --- .../locale/de/LC_MESSAGES/django.po | 236 ++++++++++-------- 1 file changed, 131 insertions(+), 105 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index affc314d6..b694b2d29 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-10 21:36+0100\n" +"POT-Creation-Date: 2025-12-14 17:22+0100\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -54,12 +54,12 @@ msgstr "Wichtige E-Mails" msgid "Displayed name" msgstr "Angezeigter Name" -#: accounts/models.py:70 coop/models.py:70 coop/models.py:445 +#: accounts/models.py:70 coop/models.py:153 coop/models.py:450 msgid "Pronouns" msgstr "Pronomen" #: accounts/models.py:71 accounts/templates/accounts/user_detail.html:71 -#: coop/models.py:72 coop/models.py:447 +#: coop/models.py:155 coop/models.py:452 #: coop/templates/coop/draftuser_detail.html:71 #: coop/templates/coop/draftuser_detail.html:142 #: coop/templates/coop/shareowner_detail.html:51 @@ -67,29 +67,29 @@ msgid "Phone number" msgstr "Telefonnummer" #: accounts/models.py:72 accounts/templates/accounts/user_detail.html:81 -#: coop/models.py:73 coop/models.py:448 +#: coop/models.py:156 coop/models.py:453 #: coop/templates/coop/draftuser_detail.html:146 #: coop/templates/coop/shareowner_detail.html:55 msgid "Birthdate" msgstr "Geburtsdatum" -#: accounts/models.py:73 coop/models.py:74 coop/models.py:449 +#: accounts/models.py:73 coop/models.py:157 coop/models.py:454 msgid "Street and house number" msgstr "Straße und Hausnummer" -#: accounts/models.py:74 coop/models.py:75 coop/models.py:450 +#: accounts/models.py:74 coop/models.py:158 coop/models.py:455 msgid "Extra address line" msgstr "Adresszusatz" -#: accounts/models.py:75 coop/models.py:76 coop/models.py:451 +#: accounts/models.py:75 coop/models.py:159 coop/models.py:456 msgid "Postcode" msgstr "Postleitzahl" -#: accounts/models.py:76 coop/models.py:77 coop/models.py:452 +#: accounts/models.py:76 coop/models.py:160 coop/models.py:457 msgid "City" msgstr "Ort" -#: accounts/models.py:77 coop/models.py:78 coop/models.py:453 +#: accounts/models.py:77 coop/models.py:161 coop/models.py:458 msgid "Country" msgstr "Land" @@ -116,7 +116,7 @@ msgid "Allow purchase tracking" msgstr "Erlaube Aufzeichnen deiner Einkäufe" #: accounts/models.py:90 accounts/templates/accounts/user_detail.html:101 -#: coop/models.py:81 coop/models.py:456 +#: coop/models.py:164 coop/models.py:461 #: coop/templates/coop/shareowner_detail.html:75 msgid "Preferred Language" msgstr "Bevorzugte Sprache" @@ -271,8 +271,8 @@ msgid "I agree that my membership card will be scanned at the checkout when I ma msgstr "Ich bin damit einverstanden, dass meine Mitgliedskarte beim Einkauf an der Kasse gescannt und somit mein Einkauf erfasst und gespeichert wird:" #: accounts/templates/accounts/purchase_tracking_card.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:168 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:172 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:177 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 msgid "Yes,No" msgstr "Ja, Nein" @@ -289,7 +289,7 @@ msgstr "Aktivieren" msgid "You can only look at your own barcode unless you have admin rights" msgstr "Du kannst nur deinen eigenen Strichcode sehen, es sei denn du hast Admin-Rechte" -#: accounts/templates/accounts/user_detail.html:20 coop/models.py:724 +#: accounts/templates/accounts/user_detail.html:20 coop/models.py:729 #: coop/templates/coop/draftuser_detail.html:86 #: coop/templates/coop/shareowner_detail.html:10 log/views.py:94 #: log/views.py:152 shifts/templates/shifts/shift_detail_printable.html:50 @@ -319,9 +319,9 @@ msgstr "Benutzername bearbeiten" #: coop/templates/coop/draftuser_detail.html:238 #: coop/templates/coop/membershipresignation_detail.html:107 #: coop/templates/coop/shareowner_detail.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:22 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:23 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:99 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:122 #: core/templates/core/featureflag_list.html:30 #: shifts/templates/shifts/shift_detail.html:61 #: shifts/templates/shifts/shift_template_detail.html:29 @@ -512,8 +512,8 @@ msgstr "Schickt mir eine Anleitung!" msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters." msgstr "Der angegebene Name ist ungültig. Er darf nur Buchstaben, Zahlen und die Symbole ./-/_ enthalten." -#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:275 -#: coop/views/shareowner.py:280 +#: accounts/views.py:124 accounts/views.py:129 coop/views/shareowner.py:280 +#: coop/views/shareowner.py:285 #, python-format msgid "Edit member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" @@ -630,7 +630,7 @@ msgstr "Anzahl zu erstellender Anteile" msgid "The end date must be later than the start date." msgstr "Das Enddatum muss später als das Start-Datum sein" -#: coop/forms.py:105 coop/models.py:462 +#: coop/forms.py:105 coop/models.py:467 msgid "Number of Shares" msgstr "Anzahl Anteile" @@ -711,168 +711,168 @@ msgstr "Absender und Empfänger der Übertragung der Anteile können nicht ident msgid "Cannot pay out, because shares have been gifted." msgstr "Die Anteile können nicht ausgezahlt werden, da sie verschenkt wurden" -#: coop/models.py:54 +#: coop/models.py:137 msgid "Is company" msgstr "Ist eine Firma" -#: coop/models.py:61 coop/models.py:436 +#: coop/models.py:144 coop/models.py:441 msgid "Administrative first name" msgstr "Amtlicher Vorname" -#: coop/models.py:63 coop/models.py:438 +#: coop/models.py:146 coop/models.py:443 msgid "Last name" msgstr "Nachname" -#: coop/models.py:65 coop/models.py:440 +#: coop/models.py:148 coop/models.py:445 msgid "Usage name" msgstr "Angezeigter Name" -#: coop/models.py:71 coop/models.py:446 +#: coop/models.py:154 coop/models.py:451 msgid "Email address" msgstr "E-Mail-Adresse" -#: coop/models.py:88 +#: coop/models.py:171 msgid "Is investing member" msgstr "Ist investierendes Mitglied" -#: coop/models.py:90 coop/models.py:476 +#: coop/models.py:173 coop/models.py:481 #: coop/templates/coop/draftuser_detail.html:176 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:48 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:167 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:57 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 msgid "Ratenzahlung" msgstr "Ratenzahlung" -#: coop/models.py:92 coop/models.py:469 +#: coop/models.py:175 coop/models.py:474 msgid "Attended Welcome Session" msgstr "An Willkommenstreffen teilgenommen" -#: coop/models.py:95 +#: coop/models.py:178 msgid "Is willing to gift a share" msgstr "Ist bereit Anteile zu verschenken" -#: coop/models.py:199 +#: coop/models.py:204 msgid "Cannot be a company and have a Tapir account" msgstr "Kann keine Firma sein und ein Tapir-Konto haben" -#: coop/models.py:215 +#: coop/models.py:220 msgid "User info should be stored in associated Tapir account" msgstr "Benutzer Infos sollen in dem Tapir Konto gespeichert warden" -#: coop/models.py:367 +#: coop/models.py:372 msgid "Not a member" msgstr "Kein Mitlied" -#: coop/models.py:368 coop/templates/coop/draftuser_detail.html:169 +#: coop/models.py:373 coop/templates/coop/draftuser_detail.html:169 msgid "Investing" msgstr "Investierend" -#: coop/models.py:369 coop/templates/coop/draftuser_detail.html:171 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:103 +#: coop/models.py:374 coop/templates/coop/draftuser_detail.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:112 #: coop/views/statistics.py:135 msgid "Active" msgstr "Aktiv" -#: coop/models.py:370 +#: coop/models.py:375 msgid "Paused" msgstr "Pausiert" -#: coop/models.py:465 +#: coop/models.py:470 msgid "Investing member" msgstr "Investierendes Mitglied" -#: coop/models.py:472 +#: coop/models.py:477 msgid "Signed Beteiligungserklärung" msgstr "Beteiligungserklärung unterschrieben" -#: coop/models.py:474 +#: coop/models.py:479 msgid "Paid Entrance Fee" msgstr "Eintrittsgeld bezahlt" -#: coop/models.py:531 +#: coop/models.py:536 msgid "Email address must be set." msgstr "Email-Adresse muss gesetzt sein." -#: coop/models.py:533 +#: coop/models.py:538 msgid "First name must be set." msgstr "Vorname muss gesetzt sein." -#: coop/models.py:535 +#: coop/models.py:540 msgid "Last name must be set." msgstr "Nachname muss gesetzt sein." -#: coop/models.py:539 +#: coop/models.py:544 msgid "Membership agreement must be signed." msgstr "Mitgliedsantrag muss unterschrieben sein." -#: coop/models.py:541 +#: coop/models.py:546 msgid "Amount of requested shares must be positive." msgstr "Die Anzahl der erwünschten Anteile muss positiv sein." -#: coop/models.py:543 +#: coop/models.py:548 msgid "Member already created." msgstr "Mitglied schon vorhanden." -#: coop/models.py:570 +#: coop/models.py:575 msgid "Paying member" msgstr "Zahlendes Mitglied" -#: coop/models.py:578 +#: coop/models.py:583 msgid "Credited member" msgstr "Empfangendes Mitglied" -#: coop/models.py:585 +#: coop/models.py:590 msgid "Amount" msgstr "Betrag" -#: coop/models.py:591 +#: coop/models.py:596 msgid "Payment date" msgstr "Zahldatum" -#: coop/models.py:594 +#: coop/models.py:599 msgid "Creation date" msgstr "Erstellungsdatum" -#: coop/models.py:599 +#: coop/models.py:604 msgid "Created by" msgstr "Erstellt durch" -#: coop/models.py:774 +#: coop/models.py:779 msgid "The cooperative buys the shares back from the member" msgstr "Die Kooperative kauft die Anteile des Mitglieds zurück." -#: coop/models.py:777 +#: coop/models.py:782 msgid "The member gifts the shares to the cooperative" msgstr "Das Mitglied schenkt die Anteile der Genossenschaft." -#: coop/models.py:779 +#: coop/models.py:784 msgid "The shares get transferred to another member" msgstr "Die Anteile werden an ein anderes Mitglied übertragen" -#: coop/models.py:782 +#: coop/models.py:787 msgid "Financial reasons" msgstr "Finanzielle Gründe" -#: coop/models.py:783 +#: coop/models.py:788 msgid "Health reasons" msgstr "Gesundheit" -#: coop/models.py:784 +#: coop/models.py:789 msgid "Distance" msgstr "Entfernung" -#: coop/models.py:785 +#: coop/models.py:790 msgid "Strategic orientation of SuperCoop" msgstr "Strategische Ausrichtung von Supercoop" -#: coop/models.py:786 +#: coop/models.py:791 msgid "Other" msgstr "Andere" -#: coop/models.py:791 coop/templates/coop/membershipresignation_detail.html:55 +#: coop/models.py:796 coop/templates/coop/membershipresignation_detail.html:55 msgid "Shareowner" msgstr "Genossenschaftsmitglied" -#: coop/models.py:810 +#: coop/models.py:815 msgid "Leave this empty if the resignation type is not a transfer to another member" msgstr "Lass das Feld leer, wenn es sich nicht um eine Übertragung auf ein anderes Mitglied handelt" @@ -911,7 +911,9 @@ msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 #: coop/templates/coop/confirm_delete_share_ownership.html:24 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:127 +#: coop/templates/coop/shareowner_confirm_delete.html:24 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:30 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:136 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 #: shifts/templates/shifts/shift_confirm_delete.html:30 msgid "Delete" @@ -973,9 +975,9 @@ msgid "List of similar members" msgstr "Liste ähnlicher Mitglieder" #: coop/templates/coop/draftuser_detail.html:69 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:31 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:88 -#: coop/views/shareowner.py:601 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 +#: coop/views/shareowner.py:634 #: shifts/templates/shifts/user_shifts_overview_tag.html:26 msgid "Status" msgstr "Status" @@ -998,7 +1000,7 @@ msgid "Shares requested" msgstr "Angeforderte Anteile" #: coop/templates/coop/draftuser_detail.html:190 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:53 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:62 #: shifts/models.py:45 msgid "Welcome Session" msgstr "Willkommenstreffen" @@ -1956,7 +1958,7 @@ msgid "General Tapir Accounts" msgstr "Allgemeine Tapir-Konten" #: coop/templates/coop/incoming_payment_list.html:9 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:160 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:169 msgid "Payments" msgstr "Zahlungen" @@ -1993,6 +1995,14 @@ msgstr "" "\n" " Reaktiviertes ausgeschiedenes Mitglied\n" +#: coop/templates/coop/log/delete_share_owner_log_entry.html:2 +#, fuzzy +#| msgid "Member can be created" +msgid "" +"\n" +"Member has been deleted.\n" +msgstr "Mitglied kann erstellt werden: " + #: coop/templates/coop/log/update_incoming_payment_log_entry.html:2 msgid "Updated payment:" msgstr "Aktualisierte Zahlung:" @@ -2246,6 +2256,26 @@ msgstr "" msgid "Organisation logo" msgstr "" +#: coop/templates/coop/shareowner_confirm_delete.html:7 +#: coop/templates/coop/shareowner_confirm_delete.html:13 +#, fuzzy +#| msgid "Confirm delete" +msgid "Confirm Delete" +msgstr "Löschen bestätigen" + +#: coop/templates/coop/shareowner_confirm_delete.html:16 +msgid "Are you sure you want to delete this member?" +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:18 +msgid "Please note that deleting this member will permanently remove their account and all associated data such as owned shares and payments. The deleted user will no longer be part of the cooperative. However, all shift records and logs will remain intact." +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:27 +#: shifts/templates/shifts/user_shifts_overview_tag.html:177 +msgid "Cancel" +msgstr "Abmelden" + #: coop/templates/coop/shareowner_detail.html:22 msgid "Go to user page" msgstr "Zur Nutzer*innen-Seite" @@ -2342,11 +2372,11 @@ msgstr "" " Mitglied #%(coop_share_owner_id)s\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:17 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:18 msgid "Membership confirmation" msgstr "Mitgliedsbestätigung" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:35 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:44 #, python-format msgid "" "\n" @@ -2354,7 +2384,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:49 #, python-format msgid "" "\n" @@ -2362,7 +2392,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 #: shifts/models.py:944 shifts/templates/shifts/shift_day_printable.html:207 #: shifts/templates/shifts/shift_day_printable.html:269 #: shifts/templates/shifts/shift_detail.html:298 @@ -2370,36 +2400,36 @@ msgstr "" msgid "Attended" msgstr "Teilgenommen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:67 #: shifts/models.py:943 msgid "Pending" msgstr "Ausstehend" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 msgid "Mark Attended" msgstr "Als teilgenommen markieren" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:83 msgid "Owned shares" msgstr "Anteile" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:82 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:91 msgid "List of shares owned by this member" msgstr "Liste der Anteile die dieses Mitglied gehören" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:86 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:95 msgid "Starts at" msgstr "Anfangsdatum" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:87 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:96 msgid "Ends at" msgstr "Endet am" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:105 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:114 msgid "Sold or future" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:130 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:139 msgid "" "\n" " Only use this to correct mistakes, i.e. if the share was\n" @@ -2413,19 +2443,19 @@ msgstr "" "Bitte nur verwenden, wenn ein tatsächlicher Fehler vorliegt, z.B. ein Anteil eingetragen wurde, der nie gekauft wurde. Wenn die Person ihren Anteil einfach an den Coop zurückverkauft hat, markiere den Anteil bitte als 'verkauft'.\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:153 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:162 msgid "Add Shares" msgstr "Anteile hinzufügen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:180 msgid "Willing to gift a share" msgstr "Bereit Anteile zu schenken" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:190 msgid "Send membership confirmation email" msgstr "Mitgliedsbestätigung per Mail senden" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:187 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:196 msgid "User is not a cooperative member." msgstr "Benutzer*in ist kein Genossenschaftsmitglied." @@ -2505,66 +2535,66 @@ msgstr "Auszahlungsende" msgid "Cancel membership of %(name)s" msgstr "Beende Mitgliedschaft von %(name)s" -#: coop/views/shareowner.py:136 coop/views/shareowner.py:141 +#: coop/views/shareowner.py:141 coop/views/shareowner.py:146 #, python-format msgid "Edit share: %(name)s" msgstr "Anteil bearbeiten: %(name)s" -#: coop/views/shareowner.py:161 coop/views/shareowner.py:166 +#: coop/views/shareowner.py:166 coop/views/shareowner.py:171 #, python-format msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:380 +#: coop/views/shareowner.py:413 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:602 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:635 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:607 +#: coop/views/shareowner.py:640 #: shifts/templates/shifts/user_shifts_overview_tag.html:79 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:615 +#: coop/views/shareowner.py:648 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:623 +#: coop/views/shareowner.py:656 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:631 +#: coop/views/shareowner.py:664 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:639 +#: coop/views/shareowner.py:672 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:646 shifts/forms.py:777 +#: coop/views/shareowner.py:679 shifts/forms.py:777 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:649 +#: coop/views/shareowner.py:682 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:652 +#: coop/views/shareowner.py:685 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:656 +#: coop/views/shareowner.py:689 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:661 +#: coop/views/shareowner.py:694 msgid "Shift Name" msgstr "Schichtname" -#: coop/views/shareowner.py:935 +#: coop/views/shareowner.py:954 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" @@ -2645,11 +2675,11 @@ msgstr "Mitgliederbüro kontaktieren" msgid "About tapir" msgstr "Über Tapir" -#: core/models.py:7 +#: core/models.py:8 msgid "Flag name" msgstr "" -#: core/models.py:10 +#: core/models.py:11 msgid "Flag value" msgstr "" @@ -4661,10 +4691,6 @@ msgstr "Solidaritäts-Schicht spenden" msgid "One of your banked shifts will be donated as a solidarity shift. Do you want to continue?" msgstr "Eine deiner gearbeiteten Schichten wird als Solidaritäts-Schicht gespendet. Möchtest du fortfahren?" -#: shifts/templates/shifts/user_shifts_overview_tag.html:177 -msgid "Cancel" -msgstr "Abmelden" - #: shifts/templates/shifts/user_shifts_overview_tag.html:180 msgid "Confirm" msgstr "" From 5598bc3bd4db6b917cd9fe69c2baa4dd4ca482c8 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:07:20 +0100 Subject: [PATCH 43/56] use soft_delete() --- tapir/coop/tests/test_shareowner_softdelete.py | 8 ++++---- tapir/coop/views/shareowner.py | 1 + tapir/core/models.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index ea22bf634..f27c77ccc 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -13,7 +13,7 @@ def test_softDelete_DeletedAt_isNotNone(self): share_owner = ShareOwnerFactory.create() self.assertIsNone(share_owner.deleted_at) - share_owner.delete() + share_owner.soft_delete() share_owner.refresh_from_db() @@ -23,7 +23,7 @@ def test_softDelete_DeletedAt_isNotNone(self): def test_softDelete_restoreDeleted_isNone(self): share_owner = ShareOwnerFactory.create() - share_owner.delete() + share_owner.soft_delete() share_owner.refresh_from_db() self.assertIsNotNone(share_owner.deleted_at) @@ -36,7 +36,7 @@ def test_softDelete_nonDeletedManager_containsNotDeletedShareowners(self): active_owner = ShareOwnerFactory.create() soft_deleted_owner = ShareOwnerFactory.create() - soft_deleted_owner.delete() + soft_deleted_owner.soft_delete() non_deleted_owners = ShareOwner.objects.all() self.assertIn(active_owner, non_deleted_owners) @@ -46,7 +46,7 @@ def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): owner1 = ShareOwnerFactory.create() owner2 = ShareOwnerFactory.create() - owner2.delete() + owner2.soft_delete() all_owners = ShareOwner.everything.all() diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 1f5148031..f43cd57ea 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -308,6 +308,7 @@ def dispatch(self, request, *args, **kwargs): @transaction.atomic def form_valid(self, form): share_owner = self.get_object() + share_owner.soft_delete() DeleteShareOwnerLogEntry().populate( share_owner=share_owner, actor=self.request.user, diff --git a/tapir/core/models.py b/tapir/core/models.py index b67bf28ff..b7b6bac1b 100644 --- a/tapir/core/models.py +++ b/tapir/core/models.py @@ -44,7 +44,7 @@ class Meta: objects = NonDeleted() everything = models.Manager() - def delete(self, using=None, keep_parents=False): + def soft_delete(self, using=None, keep_parents=False): self.deleted_at = timezone.now() self.save(update_fields=["deleted_at"], using=using) From 74b1edc154df60274a36723a1e72b7706c88e3e0 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:07:54 +0100 Subject: [PATCH 44/56] DeleteView was actually deleting ShareOwner, so form_valid returns HttpResponseRedirect now --- tapir/coop/tests/test_shareowner_delete_view.py | 2 ++ tapir/coop/views/shareowner.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py index 3d5464cca..c55a2869e 100644 --- a/tapir/coop/tests/test_shareowner_delete_view.py +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -34,6 +34,8 @@ def do_request(self): def test_ShareOwnerDeleteView_shareOwnerdeletedAt_hasDate(self): self.login_as_vorstand() tapir_user = TapirUserFactory() + self.assertIsNotNone(tapir_user.share_owner) + self.assertIsNone(tapir_user.share_owner.deleted_at) response = self.client.post( reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), follow=True, diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index f43cd57ea..7466c116f 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -314,7 +314,7 @@ def form_valid(self, form): actor=self.request.user, model=self.object, ).save() - return super().form_valid(form) + return HttpResponseRedirect(self.get_success_url()) @require_POST From 44e96df7c25e12504f912bfe632ac455996565f4 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:11:30 +0100 Subject: [PATCH 45/56] transl --- .../locale/de/LC_MESSAGES/django.po | 236 ++++++++++-------- 1 file changed, 131 insertions(+), 105 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index ea823cfd5..be9f4fbfe 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-29 10:22+0100\n" +"POT-Creation-Date: 2026-01-30 23:10+0100\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -64,12 +64,12 @@ msgstr "Wichtige E-Mails" msgid "Displayed name" msgstr "Angezeigter Name" -#: accounts/models.py:70 coop/models.py:70 coop/models.py:445 +#: accounts/models.py:70 coop/models.py:153 coop/models.py:450 msgid "Pronouns" msgstr "Pronomen" #: accounts/models.py:71 accounts/templates/accounts/user_detail.html:71 -#: coop/models.py:72 coop/models.py:447 +#: coop/models.py:155 coop/models.py:452 #: coop/templates/coop/draftuser_detail.html:71 #: coop/templates/coop/draftuser_detail.html:142 #: coop/templates/coop/shareowner_detail.html:51 @@ -77,29 +77,29 @@ msgid "Phone number" msgstr "Telefonnummer" #: accounts/models.py:72 accounts/templates/accounts/user_detail.html:81 -#: coop/models.py:73 coop/models.py:448 +#: coop/models.py:156 coop/models.py:453 #: coop/templates/coop/draftuser_detail.html:146 #: coop/templates/coop/shareowner_detail.html:55 msgid "Birthdate" msgstr "Geburtsdatum" -#: accounts/models.py:73 coop/models.py:74 coop/models.py:449 +#: accounts/models.py:73 coop/models.py:157 coop/models.py:454 msgid "Street and house number" msgstr "Straße und Hausnummer" -#: accounts/models.py:74 coop/models.py:75 coop/models.py:450 +#: accounts/models.py:74 coop/models.py:158 coop/models.py:455 msgid "Extra address line" msgstr "Adresszusatz" -#: accounts/models.py:75 coop/models.py:76 coop/models.py:451 +#: accounts/models.py:75 coop/models.py:159 coop/models.py:456 msgid "Postcode" msgstr "Postleitzahl" -#: accounts/models.py:76 coop/models.py:77 coop/models.py:452 +#: accounts/models.py:76 coop/models.py:160 coop/models.py:457 msgid "City" msgstr "Ort" -#: accounts/models.py:77 coop/models.py:78 coop/models.py:453 +#: accounts/models.py:77 coop/models.py:161 coop/models.py:458 msgid "Country" msgstr "Land" @@ -126,7 +126,7 @@ msgid "Allow purchase tracking" msgstr "Erlaube Aufzeichnen deiner Einkäufe" #: accounts/models.py:90 accounts/templates/accounts/user_detail.html:101 -#: coop/models.py:81 coop/models.py:456 +#: coop/models.py:164 coop/models.py:461 #: coop/templates/coop/shareowner_detail.html:75 msgid "Preferred Language" msgstr "Bevorzugte Sprache" @@ -289,8 +289,8 @@ msgid "I agree that my membership card will be scanned at the checkout when I ma msgstr "Ich bin damit einverstanden, dass meine Mitgliedskarte beim Einkauf an der Kasse gescannt und somit mein Einkauf erfasst und gespeichert wird:" #: accounts/templates/accounts/purchase_tracking_card.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:168 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:172 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:177 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 msgid "Yes,No" msgstr "Ja, Nein" @@ -307,7 +307,7 @@ msgstr "Aktivieren" msgid "You can only look at your own barcode unless you have admin rights" msgstr "Du kannst nur deinen eigenen Strichcode sehen, es sei denn du hast Admin-Rechte" -#: accounts/templates/accounts/user_detail.html:20 coop/models.py:724 +#: accounts/templates/accounts/user_detail.html:20 coop/models.py:729 #: coop/templates/coop/draftuser_detail.html:86 #: coop/templates/coop/shareowner_detail.html:10 log/views.py:94 #: log/views.py:152 shifts/templates/shifts/shift_detail_printable.html:50 @@ -337,9 +337,9 @@ msgstr "Benutzername bearbeiten" #: coop/templates/coop/draftuser_detail.html:238 #: coop/templates/coop/membershipresignation_detail.html:107 #: coop/templates/coop/shareowner_detail.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:22 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:23 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:99 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:122 #: core/templates/core/featureflag_list.html:30 #: shifts/templates/shifts/shift_detail.html:61 #: shifts/templates/shifts/shift_template_detail.html:29 @@ -530,8 +530,8 @@ msgstr "Schickt mir eine Anleitung!" msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters." msgstr "Der angegebene Name ist ungültig. Er darf nur Buchstaben, Zahlen und die Symbole ./-/_ enthalten." -#: accounts/views.py:129 accounts/views.py:134 coop/views/shareowner.py:276 -#: coop/views/shareowner.py:281 +#: accounts/views.py:129 accounts/views.py:134 coop/views/shareowner.py:281 +#: coop/views/shareowner.py:286 #, python-format msgid "Edit member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" @@ -648,7 +648,7 @@ msgstr "Anzahl zu erstellender Anteile" msgid "The end date must be later than the start date." msgstr "Das Enddatum muss später als das Start-Datum sein" -#: coop/forms.py:105 coop/models.py:462 +#: coop/forms.py:105 coop/models.py:467 msgid "Number of Shares" msgstr "Anzahl Anteile" @@ -729,168 +729,168 @@ msgstr "Absender und Empfänger der Übertragung der Anteile können nicht ident msgid "Cannot pay out, because shares have been gifted." msgstr "Die Anteile können nicht ausgezahlt werden, da sie verschenkt wurden" -#: coop/models.py:54 +#: coop/models.py:137 msgid "Is company" msgstr "Ist eine Firma" -#: coop/models.py:61 coop/models.py:436 +#: coop/models.py:144 coop/models.py:441 msgid "Administrative first name" msgstr "Amtlicher Vorname" -#: coop/models.py:63 coop/models.py:438 +#: coop/models.py:146 coop/models.py:443 msgid "Last name" msgstr "Nachname" -#: coop/models.py:65 coop/models.py:440 +#: coop/models.py:148 coop/models.py:445 msgid "Usage name" msgstr "Angezeigter Name" -#: coop/models.py:71 coop/models.py:446 +#: coop/models.py:154 coop/models.py:451 msgid "Email address" msgstr "E-Mail-Adresse" -#: coop/models.py:88 +#: coop/models.py:171 msgid "Is investing member" msgstr "Ist investierendes Mitglied" -#: coop/models.py:90 coop/models.py:476 +#: coop/models.py:173 coop/models.py:481 #: coop/templates/coop/draftuser_detail.html:176 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:48 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:167 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:57 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 msgid "Ratenzahlung" msgstr "Ratenzahlung" -#: coop/models.py:92 coop/models.py:469 +#: coop/models.py:175 coop/models.py:474 msgid "Attended Welcome Session" msgstr "An Willkommenstreffen teilgenommen" -#: coop/models.py:95 +#: coop/models.py:178 msgid "Is willing to gift a share" msgstr "Ist bereit Anteile zu verschenken" -#: coop/models.py:199 +#: coop/models.py:204 msgid "Cannot be a company and have a Tapir account" msgstr "Kann keine Firma sein und ein Tapir-Konto haben" -#: coop/models.py:215 +#: coop/models.py:220 msgid "User info should be stored in associated Tapir account" msgstr "Benutzer Infos sollen in dem Tapir Konto gespeichert warden" -#: coop/models.py:367 +#: coop/models.py:372 msgid "Not a member" msgstr "Kein Mitlied" -#: coop/models.py:368 coop/templates/coop/draftuser_detail.html:169 +#: coop/models.py:373 coop/templates/coop/draftuser_detail.html:169 msgid "Investing" msgstr "Investierend" -#: coop/models.py:369 coop/templates/coop/draftuser_detail.html:171 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:103 +#: coop/models.py:374 coop/templates/coop/draftuser_detail.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:112 #: coop/views/statistics.py:135 msgid "Active" msgstr "Aktiv" -#: coop/models.py:370 +#: coop/models.py:375 msgid "Paused" msgstr "Pausiert" -#: coop/models.py:465 +#: coop/models.py:470 msgid "Investing member" msgstr "Investierendes Mitglied" -#: coop/models.py:472 +#: coop/models.py:477 msgid "Signed Beteiligungserklärung" msgstr "Beteiligungserklärung unterschrieben" -#: coop/models.py:474 +#: coop/models.py:479 msgid "Paid Entrance Fee" msgstr "Eintrittsgeld bezahlt" -#: coop/models.py:531 +#: coop/models.py:536 msgid "Email address must be set." msgstr "Email-Adresse muss gesetzt sein." -#: coop/models.py:533 +#: coop/models.py:538 msgid "First name must be set." msgstr "Vorname muss gesetzt sein." -#: coop/models.py:535 +#: coop/models.py:540 msgid "Last name must be set." msgstr "Nachname muss gesetzt sein." -#: coop/models.py:539 +#: coop/models.py:544 msgid "Membership agreement must be signed." msgstr "Mitgliedsantrag muss unterschrieben sein." -#: coop/models.py:541 +#: coop/models.py:546 msgid "Amount of requested shares must be positive." msgstr "Die Anzahl der erwünschten Anteile muss positiv sein." -#: coop/models.py:543 +#: coop/models.py:548 msgid "Member already created." msgstr "Mitglied schon vorhanden." -#: coop/models.py:570 +#: coop/models.py:575 msgid "Paying member" msgstr "Zahlendes Mitglied" -#: coop/models.py:578 +#: coop/models.py:583 msgid "Credited member" msgstr "Empfangendes Mitglied" -#: coop/models.py:585 +#: coop/models.py:590 msgid "Amount" msgstr "Betrag" -#: coop/models.py:591 +#: coop/models.py:596 msgid "Payment date" msgstr "Zahldatum" -#: coop/models.py:594 +#: coop/models.py:599 msgid "Creation date" msgstr "Erstellungsdatum" -#: coop/models.py:599 +#: coop/models.py:604 msgid "Created by" msgstr "Erstellt durch" -#: coop/models.py:774 +#: coop/models.py:779 msgid "The cooperative buys the shares back from the member" msgstr "Die Kooperative kauft die Anteile des Mitglieds zurück." -#: coop/models.py:777 +#: coop/models.py:782 msgid "The member gifts the shares to the cooperative" msgstr "Das Mitglied schenkt die Anteile der Genossenschaft." -#: coop/models.py:779 +#: coop/models.py:784 msgid "The shares get transferred to another member" msgstr "Die Anteile werden an ein anderes Mitglied übertragen" -#: coop/models.py:782 +#: coop/models.py:787 msgid "Financial reasons" msgstr "Finanzielle Gründe" -#: coop/models.py:783 +#: coop/models.py:788 msgid "Health reasons" msgstr "Gesundheit" -#: coop/models.py:784 +#: coop/models.py:789 msgid "Distance" msgstr "Entfernung" -#: coop/models.py:785 +#: coop/models.py:790 msgid "Strategic orientation of SuperCoop" msgstr "Strategische Ausrichtung von Supercoop" -#: coop/models.py:786 +#: coop/models.py:791 msgid "Other" msgstr "Andere" -#: coop/models.py:791 coop/templates/coop/membershipresignation_detail.html:55 +#: coop/models.py:796 coop/templates/coop/membershipresignation_detail.html:55 msgid "Shareowner" msgstr "Genossenschaftsmitglied" -#: coop/models.py:810 +#: coop/models.py:815 msgid "Leave this empty if the resignation type is not a transfer to another member" msgstr "Lass das Feld leer, wenn es sich nicht um eine Übertragung auf ein anderes Mitglied handelt" @@ -929,7 +929,9 @@ msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 #: coop/templates/coop/confirm_delete_share_ownership.html:24 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:127 +#: coop/templates/coop/shareowner_confirm_delete.html:24 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:30 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:136 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 #: shifts/templates/shifts/shift_confirm_delete.html:30 msgid "Delete" @@ -991,9 +993,9 @@ msgid "List of similar members" msgstr "Liste ähnlicher Mitglieder" #: coop/templates/coop/draftuser_detail.html:69 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:31 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:88 -#: coop/views/shareowner.py:602 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 +#: coop/views/shareowner.py:636 #: shifts/templates/shifts/user_shifts_overview_tag.html:26 msgid "Status" msgstr "Status" @@ -1016,7 +1018,7 @@ msgid "Shares requested" msgstr "Angeforderte Anteile" #: coop/templates/coop/draftuser_detail.html:190 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:53 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:62 #: shifts/models.py:46 msgid "Welcome Session" msgstr "Willkommenstreffen" @@ -1974,7 +1976,7 @@ msgid "General Tapir Accounts" msgstr "Allgemeine Tapir-Konten" #: coop/templates/coop/incoming_payment_list.html:9 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:160 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:169 msgid "Payments" msgstr "Zahlungen" @@ -2011,6 +2013,14 @@ msgstr "" "\n" " Reaktiviertes ausgeschiedenes Mitglied\n" +#: coop/templates/coop/log/delete_share_owner_log_entry.html:2 +#, fuzzy +#| msgid "Member can be created" +msgid "" +"\n" +"Member has been deleted.\n" +msgstr "Mitglied kann erstellt werden: " + #: coop/templates/coop/log/update_incoming_payment_log_entry.html:2 msgid "Updated payment:" msgstr "Aktualisierte Zahlung:" @@ -2264,6 +2274,26 @@ msgstr "" msgid "Organisation logo" msgstr "" +#: coop/templates/coop/shareowner_confirm_delete.html:7 +#: coop/templates/coop/shareowner_confirm_delete.html:13 +#, fuzzy +#| msgid "Confirm delete" +msgid "Confirm Delete" +msgstr "Löschen bestätigen" + +#: coop/templates/coop/shareowner_confirm_delete.html:16 +msgid "Are you sure you want to delete this member?" +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:18 +msgid "Please note that deleting this member will permanently remove their account and all associated data such as owned shares and payments. The deleted user will no longer be part of the cooperative. However, all shift records and logs will remain intact." +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:27 +#: shifts/templates/shifts/user_shifts_overview_tag.html:177 +msgid "Cancel" +msgstr "Abmelden" + #: coop/templates/coop/shareowner_detail.html:22 msgid "Go to user page" msgstr "Zur Nutzer*innen-Seite" @@ -2360,11 +2390,11 @@ msgstr "" " Mitglied #%(coop_share_owner_id)s\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:17 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:18 msgid "Membership confirmation" msgstr "Mitgliedsbestätigung" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:35 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:44 #, python-format msgid "" "\n" @@ -2372,7 +2402,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:49 #, python-format msgid "" "\n" @@ -2380,7 +2410,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 #: shifts/models.py:944 shifts/templates/shifts/shift_day_printable.html:207 #: shifts/templates/shifts/shift_day_printable.html:269 #: shifts/templates/shifts/shift_detail.html:298 @@ -2388,36 +2418,36 @@ msgstr "" msgid "Attended" msgstr "Teilgenommen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:67 #: shifts/models.py:943 msgid "Pending" msgstr "Ausstehend" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 msgid "Mark Attended" msgstr "Als teilgenommen markieren" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:83 msgid "Owned shares" msgstr "Anteile" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:82 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:91 msgid "List of shares owned by this member" msgstr "Liste der Anteile die dieses Mitglied gehören" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:86 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:95 msgid "Starts at" msgstr "Anfangsdatum" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:87 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:96 msgid "Ends at" msgstr "Endet am" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:105 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:114 msgid "Sold or future" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:130 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:139 msgid "" "\n" " Only use this to correct mistakes, i.e. if the share was\n" @@ -2431,19 +2461,19 @@ msgstr "" "Bitte nur verwenden, wenn ein tatsächlicher Fehler vorliegt, z.B. ein Anteil eingetragen wurde, der nie gekauft wurde. Wenn die Person ihren Anteil einfach an den Coop zurückverkauft hat, markiere den Anteil bitte als 'verkauft'.\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:153 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:162 msgid "Add Shares" msgstr "Anteile hinzufügen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:180 msgid "Willing to gift a share" msgstr "Bereit Anteile zu schenken" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:190 msgid "Send membership confirmation email" msgstr "Mitgliedsbestätigung per Mail senden" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:187 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:196 msgid "User is not a cooperative member." msgstr "Benutzer*in ist kein Genossenschaftsmitglied." @@ -2523,66 +2553,66 @@ msgstr "Auszahlungsende" msgid "Cancel membership of %(name)s" msgstr "Beende Mitgliedschaft von %(name)s" -#: coop/views/shareowner.py:137 coop/views/shareowner.py:142 +#: coop/views/shareowner.py:142 coop/views/shareowner.py:147 #, python-format msgid "Edit share: %(name)s" msgstr "Anteil bearbeiten: %(name)s" -#: coop/views/shareowner.py:162 coop/views/shareowner.py:167 +#: coop/views/shareowner.py:167 coop/views/shareowner.py:172 #, python-format msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:381 +#: coop/views/shareowner.py:415 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:603 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:637 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:608 +#: coop/views/shareowner.py:642 #: shifts/templates/shifts/user_shifts_overview_tag.html:79 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:616 +#: coop/views/shareowner.py:650 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:624 +#: coop/views/shareowner.py:658 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:632 +#: coop/views/shareowner.py:666 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:640 +#: coop/views/shareowner.py:674 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:647 shifts/forms.py:777 +#: coop/views/shareowner.py:681 shifts/forms.py:777 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:650 +#: coop/views/shareowner.py:684 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:653 +#: coop/views/shareowner.py:687 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:657 +#: coop/views/shareowner.py:691 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:662 +#: coop/views/shareowner.py:696 msgid "Shift Name" msgstr "Schichtname" -#: coop/views/shareowner.py:978 +#: coop/views/shareowner.py:996 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" @@ -2659,11 +2689,11 @@ msgstr "Mitgliederbüro kontaktieren" msgid "About tapir" msgstr "Über Tapir" -#: core/models.py:7 +#: core/models.py:8 msgid "Flag name" msgstr "" -#: core/models.py:10 +#: core/models.py:11 msgid "Flag value" msgstr "" @@ -4748,10 +4778,6 @@ msgstr "Solidaritäts-Schicht spenden" msgid "One of your banked shifts will be donated as a solidarity shift. Do you want to continue?" msgstr "Eine deiner gearbeiteten Schichten wird als Solidaritäts-Schicht gespendet. Möchtest du fortfahren?" -#: shifts/templates/shifts/user_shifts_overview_tag.html:177 -msgid "Cancel" -msgstr "Abmelden" - #: shifts/templates/shifts/user_shifts_overview_tag.html:180 msgid "Confirm" msgstr "" From 0deafeb19a6ea13453a66312c5df644bd59875b2 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:21:27 +0100 Subject: [PATCH 46/56] fix: ShareOwnerQuerySet is stand-alone here --- tapir/coop/views/shareowner.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tapir/coop/views/shareowner.py b/tapir/coop/views/shareowner.py index 27642905a..078e1c622 100644 --- a/tapir/coop/views/shareowner.py +++ b/tapir/coop/views/shareowner.py @@ -815,7 +815,7 @@ def is_shift_partner_of_filter(queryset: ShareOwnerQuerySet, name, value: bool): ) @staticmethod - def filter_by_join_date(queryset: ShareOwner.ShareOwnerQuerySet, name, value): + def filter_by_join_date(queryset: ShareOwnerQuerySet, name, value): """Filter ShareOwners based on their first share ownership start date.""" if value.start or value.stop: # Get the earliest start date for each share owner @@ -837,9 +837,7 @@ def filter_by_join_date(queryset: ShareOwner.ShareOwnerQuerySet, name, value): return queryset.distinct() @staticmethod - def filter_by_user_date_joined( - queryset: ShareOwner.ShareOwnerQuerySet, name, value - ): + def filter_by_user_date_joined(queryset: ShareOwnerQuerySet, name, value): """Filter ShareOwners based on when their associated TapirUser account was created.""" if value.start or value.stop: queryset = queryset.filter(user__isnull=False) From b4868f985c2e3d04ee9c0c322a312a25f76332b6 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:25:50 +0100 Subject: [PATCH 47/56] don't show shareowner-card if soft-deleted --- .../coop/tags/user_coop_share_ownership_list_tag.html | 4 ++-- tapir/core/models.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html index 6a414a43f..4360cf68e 100644 --- a/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html +++ b/tapir/coop/templates/coop/tags/user_coop_share_ownership_list_tag.html @@ -4,7 +4,7 @@ {% load core %}
- {% if share_owner %} + {% if share_owner and not share_owner.is_soft_deleted %} {% blocktranslate with coop_share_owner_id=share_owner.id %} Member #{{ coop_share_owner_id }} @@ -35,7 +35,7 @@
- {% if share_owner %} + {% if share_owner and not share_owner.is_soft_deleted %}
{% translate "Status" %}:
diff --git a/tapir/core/models.py b/tapir/core/models.py index b7b6bac1b..a08e30ab3 100644 --- a/tapir/core/models.py +++ b/tapir/core/models.py @@ -54,3 +54,6 @@ def hard_delete(self, using=None, keep_parents=False): def restore(self): self.deleted_at = None self.save(update_fields=["deleted_at"]) + + def is_soft_deleted(self): + return self.deleted_at is not None From 1b035bfd0ae75f7c678118189b10c3bd88c83283 Mon Sep 17 00:00:00 2001 From: crosspolar <18083323+crosspolar@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:29:01 +0100 Subject: [PATCH 48/56] transl --- .../locale/de/LC_MESSAGES/django.po | 236 ++++++++++-------- 1 file changed, 131 insertions(+), 105 deletions(-) diff --git a/tapir/translations/locale/de/LC_MESSAGES/django.po b/tapir/translations/locale/de/LC_MESSAGES/django.po index b4497e421..2daa16ac8 100644 --- a/tapir/translations/locale/de/LC_MESSAGES/django.po +++ b/tapir/translations/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-02-07 14:00+0100\n" +"POT-Creation-Date: 2026-02-07 17:28+0100\n" "PO-Revision-Date: 2025-07-07 13:06+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: German \n" @@ -64,12 +64,12 @@ msgstr "Wichtige E-Mails" msgid "Displayed name" msgstr "Angezeigter Name" -#: accounts/models.py:70 coop/models.py:70 coop/models.py:445 +#: accounts/models.py:70 coop/models.py:153 coop/models.py:450 msgid "Pronouns" msgstr "Pronomen" #: accounts/models.py:71 accounts/templates/accounts/user_detail.html:71 -#: coop/models.py:72 coop/models.py:447 +#: coop/models.py:155 coop/models.py:452 #: coop/templates/coop/draftuser_detail.html:71 #: coop/templates/coop/draftuser_detail.html:142 #: coop/templates/coop/shareowner_detail.html:51 @@ -77,29 +77,29 @@ msgid "Phone number" msgstr "Telefonnummer" #: accounts/models.py:72 accounts/templates/accounts/user_detail.html:81 -#: coop/models.py:73 coop/models.py:448 +#: coop/models.py:156 coop/models.py:453 #: coop/templates/coop/draftuser_detail.html:146 #: coop/templates/coop/shareowner_detail.html:55 msgid "Birthdate" msgstr "Geburtsdatum" -#: accounts/models.py:73 coop/models.py:74 coop/models.py:449 +#: accounts/models.py:73 coop/models.py:157 coop/models.py:454 msgid "Street and house number" msgstr "Straße und Hausnummer" -#: accounts/models.py:74 coop/models.py:75 coop/models.py:450 +#: accounts/models.py:74 coop/models.py:158 coop/models.py:455 msgid "Extra address line" msgstr "Adresszusatz" -#: accounts/models.py:75 coop/models.py:76 coop/models.py:451 +#: accounts/models.py:75 coop/models.py:159 coop/models.py:456 msgid "Postcode" msgstr "Postleitzahl" -#: accounts/models.py:76 coop/models.py:77 coop/models.py:452 +#: accounts/models.py:76 coop/models.py:160 coop/models.py:457 msgid "City" msgstr "Ort" -#: accounts/models.py:77 coop/models.py:78 coop/models.py:453 +#: accounts/models.py:77 coop/models.py:161 coop/models.py:458 msgid "Country" msgstr "Land" @@ -126,7 +126,7 @@ msgid "Allow purchase tracking" msgstr "Erlaube Aufzeichnen deiner Einkäufe" #: accounts/models.py:90 accounts/templates/accounts/user_detail.html:101 -#: coop/models.py:81 coop/models.py:456 +#: coop/models.py:164 coop/models.py:461 #: coop/templates/coop/shareowner_detail.html:75 msgid "Preferred Language" msgstr "Bevorzugte Sprache" @@ -289,8 +289,8 @@ msgid "I agree that my membership card will be scanned at the checkout when I ma msgstr "Ich bin damit einverstanden, dass meine Mitgliedskarte beim Einkauf an der Kasse gescannt und somit mein Einkauf erfasst und gespeichert wird:" #: accounts/templates/accounts/purchase_tracking_card.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:168 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:172 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:177 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 msgid "Yes,No" msgstr "Ja, Nein" @@ -307,7 +307,7 @@ msgstr "Aktivieren" msgid "You can only look at your own barcode unless you have admin rights" msgstr "Du kannst nur deinen eigenen Strichcode sehen, es sei denn du hast Admin-Rechte" -#: accounts/templates/accounts/user_detail.html:20 coop/models.py:724 +#: accounts/templates/accounts/user_detail.html:20 coop/models.py:729 #: coop/templates/coop/draftuser_detail.html:86 #: coop/templates/coop/shareowner_detail.html:10 log/views.py:94 #: log/views.py:152 shifts/templates/shifts/shift_detail_printable.html:50 @@ -337,9 +337,9 @@ msgstr "Benutzername bearbeiten" #: coop/templates/coop/draftuser_detail.html:238 #: coop/templates/coop/membershipresignation_detail.html:107 #: coop/templates/coop/shareowner_detail.html:30 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:22 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:90 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:113 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:23 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:99 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:122 #: core/templates/core/featureflag_list.html:30 #: shifts/templates/shifts/shift_detail.html:61 #: shifts/templates/shifts/shift_template_detail.html:29 @@ -530,8 +530,8 @@ msgstr "Schickt mir eine Anleitung!" msgid "Enter a valid username. This value may contain only letters, numbers, and ./-/_ characters." msgstr "Der angegebene Name ist ungültig. Er darf nur Buchstaben, Zahlen und die Symbole ./-/_ enthalten." -#: accounts/views.py:129 accounts/views.py:134 coop/views/shareowner.py:276 -#: coop/views/shareowner.py:281 +#: accounts/views.py:129 accounts/views.py:134 coop/views/shareowner.py:281 +#: coop/views/shareowner.py:286 #, python-format msgid "Edit member: %(name)s" msgstr "Mitglied bearbeiten: %(name)s" @@ -648,7 +648,7 @@ msgstr "Anzahl zu erstellender Anteile" msgid "The end date must be later than the start date." msgstr "Das Enddatum muss später als das Start-Datum sein" -#: coop/forms.py:105 coop/models.py:462 +#: coop/forms.py:105 coop/models.py:467 msgid "Number of Shares" msgstr "Anzahl Anteile" @@ -729,168 +729,168 @@ msgstr "Absender und Empfänger der Übertragung der Anteile können nicht ident msgid "Cannot pay out, because shares have been gifted." msgstr "Die Anteile können nicht ausgezahlt werden, da sie verschenkt wurden" -#: coop/models.py:54 +#: coop/models.py:137 msgid "Is company" msgstr "Ist eine Firma" -#: coop/models.py:61 coop/models.py:436 +#: coop/models.py:144 coop/models.py:441 msgid "Administrative first name" msgstr "Amtlicher Vorname" -#: coop/models.py:63 coop/models.py:438 +#: coop/models.py:146 coop/models.py:443 msgid "Last name" msgstr "Nachname" -#: coop/models.py:65 coop/models.py:440 +#: coop/models.py:148 coop/models.py:445 msgid "Usage name" msgstr "Angezeigter Name" -#: coop/models.py:71 coop/models.py:446 +#: coop/models.py:154 coop/models.py:451 msgid "Email address" msgstr "E-Mail-Adresse" -#: coop/models.py:88 +#: coop/models.py:171 msgid "Is investing member" msgstr "Ist investierendes Mitglied" -#: coop/models.py:90 coop/models.py:476 +#: coop/models.py:173 coop/models.py:481 #: coop/templates/coop/draftuser_detail.html:176 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:48 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:167 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:57 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:176 msgid "Ratenzahlung" msgstr "Ratenzahlung" -#: coop/models.py:92 coop/models.py:469 +#: coop/models.py:175 coop/models.py:474 msgid "Attended Welcome Session" msgstr "An Willkommenstreffen teilgenommen" -#: coop/models.py:95 +#: coop/models.py:178 msgid "Is willing to gift a share" msgstr "Ist bereit Anteile zu verschenken" -#: coop/models.py:199 +#: coop/models.py:204 msgid "Cannot be a company and have a Tapir account" msgstr "Kann keine Firma sein und ein Tapir-Konto haben" -#: coop/models.py:215 +#: coop/models.py:220 msgid "User info should be stored in associated Tapir account" msgstr "Benutzer Infos sollen in dem Tapir Konto gespeichert warden" -#: coop/models.py:367 +#: coop/models.py:372 msgid "Not a member" msgstr "Kein Mitlied" -#: coop/models.py:368 coop/templates/coop/draftuser_detail.html:169 +#: coop/models.py:373 coop/templates/coop/draftuser_detail.html:169 msgid "Investing" msgstr "Investierend" -#: coop/models.py:369 coop/templates/coop/draftuser_detail.html:171 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:103 +#: coop/models.py:374 coop/templates/coop/draftuser_detail.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:112 #: coop/views/statistics.py:135 msgid "Active" msgstr "Aktiv" -#: coop/models.py:370 +#: coop/models.py:375 msgid "Paused" msgstr "Pausiert" -#: coop/models.py:465 +#: coop/models.py:470 msgid "Investing member" msgstr "Investierendes Mitglied" -#: coop/models.py:472 +#: coop/models.py:477 msgid "Signed Beteiligungserklärung" msgstr "Beteiligungserklärung unterschrieben" -#: coop/models.py:474 +#: coop/models.py:479 msgid "Paid Entrance Fee" msgstr "Eintrittsgeld bezahlt" -#: coop/models.py:531 +#: coop/models.py:536 msgid "Email address must be set." msgstr "Email-Adresse muss gesetzt sein." -#: coop/models.py:533 +#: coop/models.py:538 msgid "First name must be set." msgstr "Vorname muss gesetzt sein." -#: coop/models.py:535 +#: coop/models.py:540 msgid "Last name must be set." msgstr "Nachname muss gesetzt sein." -#: coop/models.py:539 +#: coop/models.py:544 msgid "Membership agreement must be signed." msgstr "Mitgliedsantrag muss unterschrieben sein." -#: coop/models.py:541 +#: coop/models.py:546 msgid "Amount of requested shares must be positive." msgstr "Die Anzahl der erwünschten Anteile muss positiv sein." -#: coop/models.py:543 +#: coop/models.py:548 msgid "Member already created." msgstr "Mitglied schon vorhanden." -#: coop/models.py:570 +#: coop/models.py:575 msgid "Paying member" msgstr "Zahlendes Mitglied" -#: coop/models.py:578 +#: coop/models.py:583 msgid "Credited member" msgstr "Empfangendes Mitglied" -#: coop/models.py:585 +#: coop/models.py:590 msgid "Amount" msgstr "Betrag" -#: coop/models.py:591 +#: coop/models.py:596 msgid "Payment date" msgstr "Zahldatum" -#: coop/models.py:594 +#: coop/models.py:599 msgid "Creation date" msgstr "Erstellungsdatum" -#: coop/models.py:599 +#: coop/models.py:604 msgid "Created by" msgstr "Erstellt durch" -#: coop/models.py:774 +#: coop/models.py:779 msgid "The cooperative buys the shares back from the member" msgstr "Die Kooperative kauft die Anteile des Mitglieds zurück." -#: coop/models.py:777 +#: coop/models.py:782 msgid "The member gifts the shares to the cooperative" msgstr "Das Mitglied schenkt die Anteile der Genossenschaft." -#: coop/models.py:779 +#: coop/models.py:784 msgid "The shares get transferred to another member" msgstr "Die Anteile werden an ein anderes Mitglied übertragen" -#: coop/models.py:782 +#: coop/models.py:787 msgid "Financial reasons" msgstr "Finanzielle Gründe" -#: coop/models.py:783 +#: coop/models.py:788 msgid "Health reasons" msgstr "Gesundheit" -#: coop/models.py:784 +#: coop/models.py:789 msgid "Distance" msgstr "Entfernung" -#: coop/models.py:785 +#: coop/models.py:790 msgid "Strategic orientation of SuperCoop" msgstr "Strategische Ausrichtung von Supercoop" -#: coop/models.py:786 +#: coop/models.py:791 msgid "Other" msgstr "Andere" -#: coop/models.py:791 coop/templates/coop/membershipresignation_detail.html:55 +#: coop/models.py:796 coop/templates/coop/membershipresignation_detail.html:55 msgid "Shareowner" msgstr "Genossenschaftsmitglied" -#: coop/models.py:810 +#: coop/models.py:815 msgid "Leave this empty if the resignation type is not a transfer to another member" msgstr "Lass das Feld leer, wenn es sich nicht um eine Übertragung auf ein anderes Mitglied handelt" @@ -929,7 +929,9 @@ msgstr "Löschen bestätigen" #: coop/templates/coop/confirm_delete_incoming_payment.html:31 #: coop/templates/coop/confirm_delete_share_ownership.html:24 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:127 +#: coop/templates/coop/shareowner_confirm_delete.html:24 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:30 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:136 #: financingcampaign/templates/financingcampaign/confirm_delete.html:24 #: shifts/templates/shifts/shift_confirm_delete.html:30 msgid "Delete" @@ -991,9 +993,9 @@ msgid "List of similar members" msgstr "Liste ähnlicher Mitglieder" #: coop/templates/coop/draftuser_detail.html:69 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:31 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:88 -#: coop/views/shareowner.py:602 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:97 +#: coop/views/shareowner.py:636 #: shifts/templates/shifts/user_shifts_overview_tag.html:26 msgid "Status" msgstr "Status" @@ -1016,7 +1018,7 @@ msgid "Shares requested" msgstr "Angeforderte Anteile" #: coop/templates/coop/draftuser_detail.html:190 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:53 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:62 #: shifts/models.py:46 msgid "Welcome Session" msgstr "Willkommenstreffen" @@ -1974,7 +1976,7 @@ msgid "General Tapir Accounts" msgstr "Allgemeine Tapir-Konten" #: coop/templates/coop/incoming_payment_list.html:9 -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:160 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:169 msgid "Payments" msgstr "Zahlungen" @@ -2011,6 +2013,14 @@ msgstr "" "\n" " Reaktiviertes ausgeschiedenes Mitglied\n" +#: coop/templates/coop/log/delete_share_owner_log_entry.html:2 +#, fuzzy +#| msgid "Member can be created" +msgid "" +"\n" +"Member has been deleted.\n" +msgstr "Mitglied kann erstellt werden: " + #: coop/templates/coop/log/update_incoming_payment_log_entry.html:2 msgid "Updated payment:" msgstr "Aktualisierte Zahlung:" @@ -2264,6 +2274,26 @@ msgstr "" msgid "Organisation logo" msgstr "" +#: coop/templates/coop/shareowner_confirm_delete.html:7 +#: coop/templates/coop/shareowner_confirm_delete.html:13 +#, fuzzy +#| msgid "Confirm delete" +msgid "Confirm Delete" +msgstr "Löschen bestätigen" + +#: coop/templates/coop/shareowner_confirm_delete.html:16 +msgid "Are you sure you want to delete this member?" +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:18 +msgid "Please note that deleting this member will permanently remove their account and all associated data such as owned shares and payments. The deleted user will no longer be part of the cooperative. However, all shift records and logs will remain intact." +msgstr "" + +#: coop/templates/coop/shareowner_confirm_delete.html:27 +#: shifts/templates/shifts/user_shifts_overview_tag.html:177 +msgid "Cancel" +msgstr "Abmelden" + #: coop/templates/coop/shareowner_detail.html:22 msgid "Go to user page" msgstr "Zur Nutzer*innen-Seite" @@ -2360,11 +2390,11 @@ msgstr "" " Mitglied #%(coop_share_owner_id)s\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:17 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:18 msgid "Membership confirmation" msgstr "Mitgliedsbestätigung" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:35 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:44 #, python-format msgid "" "\n" @@ -2372,7 +2402,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:40 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:49 #, python-format msgid "" "\n" @@ -2380,7 +2410,7 @@ msgid "" " " msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:56 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 #: shifts/models.py:943 shifts/templates/shifts/shift_day_printable.html:207 #: shifts/templates/shifts/shift_day_printable.html:269 #: shifts/templates/shifts/shift_detail.html:298 @@ -2388,36 +2418,36 @@ msgstr "" msgid "Attended" msgstr "Teilgenommen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:58 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:67 #: shifts/models.py:942 msgid "Pending" msgstr "Ausstehend" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:65 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 msgid "Mark Attended" msgstr "Als teilgenommen markieren" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:74 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:83 msgid "Owned shares" msgstr "Anteile" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:82 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:91 msgid "List of shares owned by this member" msgstr "Liste der Anteile die dieses Mitglied gehören" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:86 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:95 msgid "Starts at" msgstr "Anfangsdatum" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:87 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:96 msgid "Ends at" msgstr "Endet am" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:105 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:114 msgid "Sold or future" msgstr "" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:130 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:139 msgid "" "\n" " Only use this to correct mistakes, i.e. if the share was\n" @@ -2431,19 +2461,19 @@ msgstr "" "Bitte nur verwenden, wenn ein tatsächlicher Fehler vorliegt, z.B. ein Anteil eingetragen wurde, der nie gekauft wurde. Wenn die Person ihren Anteil einfach an den Coop zurückverkauft hat, markiere den Anteil bitte als 'verkauft'.\n" " " -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:153 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:162 msgid "Add Shares" msgstr "Anteile hinzufügen" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:171 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:180 msgid "Willing to gift a share" msgstr "Bereit Anteile zu schenken" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:181 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:190 msgid "Send membership confirmation email" msgstr "Mitgliedsbestätigung per Mail senden" -#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:187 +#: coop/templates/coop/tags/user_coop_share_ownership_list_tag.html:196 msgid "User is not a cooperative member." msgstr "Benutzer*in ist kein Genossenschaftsmitglied." @@ -2523,66 +2553,66 @@ msgstr "Auszahlungsende" msgid "Cancel membership of %(name)s" msgstr "Beende Mitgliedschaft von %(name)s" -#: coop/views/shareowner.py:137 coop/views/shareowner.py:142 +#: coop/views/shareowner.py:142 coop/views/shareowner.py:147 #, python-format msgid "Edit share: %(name)s" msgstr "Anteil bearbeiten: %(name)s" -#: coop/views/shareowner.py:162 coop/views/shareowner.py:167 +#: coop/views/shareowner.py:167 coop/views/shareowner.py:172 #, python-format msgid "Add shares to %(name)s" msgstr "Anteile zu %(name)s hinzufügen" -#: coop/views/shareowner.py:381 +#: coop/views/shareowner.py:415 msgid "Membership confirmation email sent." msgstr "Mitgliedsbestätigung per Mail gesendet." -#: coop/views/shareowner.py:603 shifts/templates/shifts/shift_filters.html:45 +#: coop/views/shareowner.py:637 shifts/templates/shifts/shift_filters.html:45 msgid "Any" msgstr "Alle" -#: coop/views/shareowner.py:608 +#: coop/views/shareowner.py:642 #: shifts/templates/shifts/user_shifts_overview_tag.html:79 msgid "Shift Status" msgstr "Schichtstatus" -#: coop/views/shareowner.py:616 +#: coop/views/shareowner.py:650 msgid "Is registered to an ABCD-slot that requires a qualification" msgstr "Ist für eine ABCD-Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:624 +#: coop/views/shareowner.py:658 msgid "Is registered to a slot that requires a qualification" msgstr "Ist für eine Schicht eingetragen, die Qualifikation erfordert" -#: coop/views/shareowner.py:632 +#: coop/views/shareowner.py:666 msgid "Has qualification" msgstr "Hat Qualifikation" -#: coop/views/shareowner.py:640 +#: coop/views/shareowner.py:674 msgid "Does not have qualification" msgstr "Hat die Qualifikation nicht" -#: coop/views/shareowner.py:647 shifts/forms.py:777 +#: coop/views/shareowner.py:681 shifts/forms.py:777 msgid "ABCD Week" msgstr "ABCD-Woche" -#: coop/views/shareowner.py:650 +#: coop/views/shareowner.py:684 msgid "Is fully paid" msgstr "Hat vollständig bezahlt" -#: coop/views/shareowner.py:653 +#: coop/views/shareowner.py:687 msgid "Name or member ID" msgstr "Name oder Mitgliedsnummer" -#: coop/views/shareowner.py:657 +#: coop/views/shareowner.py:691 msgid "Is currently exempted from shifts" msgstr "Ist derzeit von der Schichtarbeit befreit" -#: coop/views/shareowner.py:662 +#: coop/views/shareowner.py:696 msgid "Shift Name" msgstr "Schichtname" -#: coop/views/shareowner.py:978 +#: coop/views/shareowner.py:996 msgctxt "Willing to give a share" msgid "No" msgstr "Nein" @@ -2659,11 +2689,11 @@ msgstr "Mitgliederbüro kontaktieren" msgid "About tapir" msgstr "Über Tapir" -#: core/models.py:7 +#: core/models.py:8 msgid "Flag name" msgstr "" -#: core/models.py:10 +#: core/models.py:11 msgid "Flag value" msgstr "" @@ -4717,10 +4747,6 @@ msgstr "Solidaritäts-Schicht spenden" msgid "One of your banked shifts will be donated as a solidarity shift. Do you want to continue?" msgstr "Eine deiner gearbeiteten Schichten wird als Solidaritäts-Schicht gespendet. Möchtest du fortfahren?" -#: shifts/templates/shifts/user_shifts_overview_tag.html:177 -msgid "Cancel" -msgstr "Abmelden" - #: shifts/templates/shifts/user_shifts_overview_tag.html:180 msgid "Confirm" msgstr "" From 737523c10d456b79ea2b10781a14ffdb283581b5 Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:18:10 +0100 Subject: [PATCH 49/56] Update tapir/coop/tests/test_shareowner_delete_view.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_delete_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py index c55a2869e..68ade13bc 100644 --- a/tapir/coop/tests/test_shareowner_delete_view.py +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -31,7 +31,7 @@ def do_request(self): follow=True, ) - def test_ShareOwnerDeleteView_shareOwnerdeletedAt_hasDate(self): + def test_ShareOwnerDeleteView_default_setsDeletedAtField(self): self.login_as_vorstand() tapir_user = TapirUserFactory() self.assertIsNotNone(tapir_user.share_owner) From d84fb36ba151fc2e549cb2e2305ef5cec4156880 Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:19:45 +0100 Subject: [PATCH 50/56] Update tapir/coop/tests/test_shareowner_softdelete.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_softdelete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index f27c77ccc..d2f74b4f4 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -32,7 +32,7 @@ def test_softDelete_restoreDeleted_isNone(self): self.assertIsNone(share_owner.deleted_at) - def test_softDelete_nonDeletedManager_containsNotDeletedShareowners(self): + def test_shareOwnerDefaultManager_default_containsOnlyNotDeletedShareOwners(self): active_owner = ShareOwnerFactory.create() soft_deleted_owner = ShareOwnerFactory.create() From e99812693116c143c05a44f3d054ffd4a9af1bbd Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:20:07 +0100 Subject: [PATCH 51/56] Update tapir/coop/tests/test_shareowner_softdelete.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_softdelete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index d2f74b4f4..75a81b1b8 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -20,7 +20,7 @@ def test_softDelete_DeletedAt_isNotNone(self): self.assertIsNotNone(share_owner.deleted_at) self.assertLessEqual(share_owner.deleted_at, timezone.now()) - def test_softDelete_restoreDeleted_isNone(self): + def test_restore_default_deletedAtSetToNone(self): share_owner = ShareOwnerFactory.create() share_owner.soft_delete() From 99bcb6fce321fbf7c41726dd4d59c3e4d83e6352 Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:20:26 +0100 Subject: [PATCH 52/56] Update tapir/coop/tests/test_shareowner_softdelete.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_softdelete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index 75a81b1b8..fe990faf0 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -53,7 +53,7 @@ def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): self.assertIn(owner1, all_owners) self.assertIn(owner2, all_owners) - def test_hardDelete_shareOwner_isfullyDeleted(self): + def test_hardDelete_default_shareOwnerHardDeleted(self): share_owner = ShareOwnerFactory.create() all_users = ShareOwner.everything.all() self.assertIn(share_owner, all_users) From f4bb83cdf5648d89787be31626360b62d9e561bb Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:20:44 +0100 Subject: [PATCH 53/56] Update tapir/coop/tests/test_shareowner_delete_view.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_delete_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py index 68ade13bc..96646e47d 100644 --- a/tapir/coop/tests/test_shareowner_delete_view.py +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -45,7 +45,7 @@ def test_ShareOwnerDeleteView_default_setsDeletedAtField(self): self.assertIsNotNone(tapir_user.share_owner.deleted_at) self.assertEqual(DeleteShareOwnerLogEntry.objects.count(), 1) - def test_ShareOwnerDeleteView_normalShareOwner_cannotDeleteItself(self): + def test_ShareOwnerDeleteView_normalMemberTriesToDeleteItself_doesntDelete(self): tapir_user = self.login_as_normal_user() response = self.client.post( reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), From 19ddea859dac41d7a69a13cd69317fc56fb3df23 Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:21:45 +0100 Subject: [PATCH 54/56] Update tapir/coop/tests/test_shareowner_delete_view.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_delete_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_delete_view.py b/tapir/coop/tests/test_shareowner_delete_view.py index 96646e47d..a31c47ba8 100644 --- a/tapir/coop/tests/test_shareowner_delete_view.py +++ b/tapir/coop/tests/test_shareowner_delete_view.py @@ -56,7 +56,7 @@ def test_ShareOwnerDeleteView_normalMemberTriesToDeleteItself_doesntDelete(self) self.assertIsNone(tapir_user.share_owner.deleted_at) self.assertEqual(DeleteShareOwnerLogEntry.objects.count(), 0) - def test_ShareOwnerDeleteView_vorstandShareOwner_cannotDeleteItself(self): + def test_ShareOwnerDeleteView_adminTriesToDeleteItself_doesntDelete(self): tapir_user = self.login_as_vorstand() response = self.client.post( reverse("coop:shareowner_delete", args=[tapir_user.share_owner.id]), From d2d34911cb64a525296da6fcc5a0d190c3ee2a49 Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:22:05 +0100 Subject: [PATCH 55/56] Update tapir/coop/tests/test_shareowner_softdelete.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_softdelete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index fe990faf0..72b9ff110 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -9,7 +9,7 @@ class TestShareOwnershipSoftDelete(TapirFactoryTestBase): - def test_softDelete_DeletedAt_isNotNone(self): + def test_softDelete_default_setsDeletedAt(self): share_owner = ShareOwnerFactory.create() self.assertIsNone(share_owner.deleted_at) From caee031b4a6c96b2c499d556e3e8964d3669427e Mon Sep 17 00:00:00 2001 From: Frederik <18083323+crosspolar@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:22:21 +0100 Subject: [PATCH 56/56] Update tapir/coop/tests/test_shareowner_softdelete.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophile MADET --- tapir/coop/tests/test_shareowner_softdelete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapir/coop/tests/test_shareowner_softdelete.py b/tapir/coop/tests/test_shareowner_softdelete.py index 72b9ff110..ba4f36794 100644 --- a/tapir/coop/tests/test_shareowner_softdelete.py +++ b/tapir/coop/tests/test_shareowner_softdelete.py @@ -42,7 +42,7 @@ def test_shareOwnerDefaultManager_default_containsOnlyNotDeletedShareOwners(self self.assertIn(active_owner, non_deleted_owners) self.assertNotIn(soft_deleted_owner, non_deleted_owners) - def test_softDelete_everythingManager_containsAlsoDeletedShareowners(self): + def test_shareOwnerEverythingManager_default_containsAlsoDeletedShareowners(self): owner1 = ShareOwnerFactory.create() owner2 = ShareOwnerFactory.create()