diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 34caec6d9..43678fcb6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,8 +100,9 @@ The class name is the convention for the word in texts, followed by how to write | DraftUser | `draft_user` | Also called Applicant. Represents a person that expressed interest in joining but that hasn't completed the subscription process yet. Gets deleted when ShareOwner is created. | | ShareOwner | `share_owner` | Represents a person or a company that is either currently owning at least a share, or has owned shares in the past. Therefore they are or have been a member of the cooperative. They may not be active, for example investing members or someone who sold their shares. | | TapirUser | `tapir_user` | Represents a person with a user account. Accounts are linked between Tapir and the Wiki for example. Gets created when the member becomes active (part of the shift system etc.), but can become inactive. | -| Shift | | Represents a shift with a specific date and time (for example, 18/06/21 10:00 to 13:00). Can be a one-time activity or an instance of a ShiftTemplate | -| ShiftTemplate | | Represents the recurring aspect of a shift in the ABCD system. For example helping at the shop on Tuesday, 10:00 to 13:00, on Week C. It has a weekday (Tuesday) and a time, but no date (18/06/21) | +| Shift | | Represents a shift with a specific date and time (for example, 18/06/21 10:00 to 13:00). Can be a one-time activity or an instance of a ShiftTemplate | +| ShiftTemplate | | Represents the recurring aspect of a shift in the ABCD system. For example helping at the shop on Tuesday, 10:00 to 13:00, on Week C. It has a weekday (Tuesday) and a time, but no date (18/06/21) | +| ShiftAttendance | 'attendances', 'shift_attendances' | Represents the registration of a member to a slot. An attendance is always in one of the states defined in the state variable and the ShiftAttendance.State enum class. This is used for tracking who is coming to which shift but also if members are attending enough shifts. See ShiftAttendance.update_shift_account_entry. | ### Django Shell diff --git a/tapir/accounts/templates/accounts/email/create_account_reminder.body.html b/tapir/accounts/templates/accounts/email/create_account_reminder.body.html index f1ad7329d..2a652cac7 100644 --- a/tapir/accounts/templates/accounts/email/create_account_reminder.body.html +++ b/tapir/accounts/templates/accounts/email/create_account_reminder.body.html @@ -18,8 +18,8 @@ or visit SuperCoop. Feel free to drop by and meet other SuperCoopies in person! Opening hours are::
diff --git a/tapir/coop/forms.py b/tapir/coop/forms.py index c3327dae0..a660edec2 100644 --- a/tapir/coop/forms.py +++ b/tapir/coop/forms.py @@ -1,4 +1,3 @@ -from dateutil.relativedelta import relativedelta from django import forms from django.core.exceptions import ValidationError from django.forms import DateField, IntegerField @@ -289,7 +288,6 @@ def clean(self): cleaned_data = super().clean() share_owner: ShareOwner = cleaned_data.get("share_owner") resignation_type = cleaned_data.get("resignation_type") - cancellation_date = cleaned_data.get("cancellation_date") transferring_shares_to = cleaned_data.get("transferring_shares_to") paid_out = cleaned_data.get("paid_out") @@ -298,20 +296,15 @@ def clean(self): self.validate_duplicates(share_owner, transferring_shares_to) self.validate_if_gifted(resignation_type, paid_out) - if resignation_type == MembershipResignation.ResignationType.BUY_BACK: - self.cleaned_data["pay_out_day"] = cancellation_date + relativedelta( - day=31, month=12, years=3 - ) - else: - self.cleaned_data["pay_out_day"] = cancellation_date - self.cleaned_data["paid_out"] = True - return cleaned_data def validate_share_owner(self, share_owner): - if MembershipResignation.objects.filter( - share_owner__id=share_owner.id - ).exists(): + if ( + self.instance.pk is None + and MembershipResignation.objects.filter( + share_owner__id=share_owner.id + ).exists() + ): self.add_error( "share_owner", ValidationError(_("This member is already resigned.")), diff --git a/tapir/coop/migrations/0047_membershipresignationdeletelogentry_and_more.py b/tapir/coop/migrations/0047_membershipresignationdeletelogentry_and_more.py new file mode 100644 index 000000000..60c88f00c --- /dev/null +++ b/tapir/coop/migrations/0047_membershipresignationdeletelogentry_and_more.py @@ -0,0 +1,49 @@ +# Generated by Django 5.1.1 on 2024-11-09 16:53 + +import django.contrib.postgres.fields.hstore +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("coop", "0046_deleteincomingpaymentlogentry"), + ("log", "0007_auto_20240702_1748"), + ] + + operations = [ + migrations.CreateModel( + name="MembershipResignationDeleteLogEntry", + 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",), + ), + migrations.AlterField( + model_name="membershipresignation", + name="transferring_shares_to", + field=models.ForeignKey( + help_text="Leave this empty if the resignation type is not a transfer to another member", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="owner_to_transfer", + to="coop.shareowner", + verbose_name="OwnerToTransfer", + ), + ), + ] diff --git a/tapir/coop/models.py b/tapir/coop/models.py index 25ae0419d..464471a1b 100644 --- a/tapir/coop/models.py +++ b/tapir/coop/models.py @@ -807,7 +807,7 @@ class ResignationType(models.TextChoices): pay_out_day = models.DateField(null=True) cancellation_reason = models.CharField(max_length=1000) resignation_type = models.CharField(choices=ResignationType.choices, max_length=50) - transferring_shares_to = models.OneToOneField( + transferring_shares_to = models.ForeignKey( ShareOwner, on_delete=models.deletion.PROTECT, verbose_name="OwnerToTransfer", @@ -860,3 +860,16 @@ def populate( old_frozen=old_frozen, new_frozen=new_frozen, ) + + +class MembershipResignationDeleteLogEntry(ModelLogEntry): + template_name = "coop/log/delete_resignmember_log_entry.html" + + def populate( + self, + actor: User, + model: MembershipResignation, + ): + return super().populate_base( + actor=actor, share_owner=model.share_owner, model=model + ) diff --git a/tapir/coop/services/MembershipResignationService.py b/tapir/coop/services/MembershipResignationService.py index e609b5857..dc8d5a184 100644 --- a/tapir/coop/services/MembershipResignationService.py +++ b/tapir/coop/services/MembershipResignationService.py @@ -1,4 +1,5 @@ import datetime +from dateutil.relativedelta import relativedelta from django.db import transaction @@ -14,22 +15,27 @@ class MembershipResignationService: @staticmethod @transaction.atomic - def update_shifts_and_shares(resignation: MembershipResignation): + def update_shifts_and_shares_and_pay_out_day(resignation: MembershipResignation): tapir_user: TapirUser = getattr(resignation.share_owner, "user", None) shares = ShareOwnership.objects.filter(share_owner=resignation.share_owner) match resignation.resignation_type: case MembershipResignation.ResignationType.BUY_BACK: - new_end_date = resignation.cancellation_date.replace(day=31, month=12) - new_end_date = new_end_date.replace(year=new_end_date.year + 3) + new_end_date = resignation.cancellation_date + relativedelta( + years=+3, day=31, month=12 + ) resignation.pay_out_day = new_end_date resignation.save() shares.update(end_date=new_end_date) return case MembershipResignation.ResignationType.GIFT_TO_COOP: + resignation.pay_out_day = resignation.cancellation_date + resignation.save() shares.update(end_date=resignation.cancellation_date) case MembershipResignation.ResignationType.TRANSFER: + resignation.pay_out_day = resignation.cancellation_date + resignation.save() shares_to_create = [ ShareOwnership( share_owner=resignation.transferring_shares_to, @@ -38,7 +44,6 @@ def update_shifts_and_shares(resignation: MembershipResignation): for _ in shares ] ShareOwnership.objects.bulk_create(shares_to_create) - shares.update(end_date=resignation.cancellation_date) case _: raise ValueError( f"Unknown resignation type: {resignation.resignation_type}" @@ -58,9 +63,9 @@ def update_shifts(tapir_user: TapirUser, resignation: MembershipResignation): ) for attendance_template in ShiftAttendanceTemplate.objects.filter( - user=tapir_user + user=tapir_user, ): - attendance_template.cancel_attendances(start_date) + attendance_template.cancel_attendances(starting_from=start_date) attendance_template.delete() attendances = ShiftAttendance.objects.filter( @@ -72,8 +77,8 @@ def update_shifts(tapir_user: TapirUser, resignation: MembershipResignation): @staticmethod @transaction.atomic - def delete_end_dates(member: MembershipResignation): - ShareOwnership.objects.filter(share_owner=member.share_owner).update( + def delete_end_dates(resignation: MembershipResignation): + ShareOwnership.objects.filter(share_owner=resignation.share_owner).update( end_date=None ) @@ -83,15 +88,12 @@ def delete_shareowner_membershippauses(resignation: MembershipResignation): share_owner=resignation.share_owner ): if pause.end_date is not None: - if resignation.pay_out_day is not None: - if resignation.pay_out_day <= pause.end_date: - pause.update(end_date=resignation.pay_out_day) - elif pause.start_date > resignation.pay_out_day: - pause.delete() - else: - if resignation.cancellation_date <= pause.end_date: - pause.update(end_date=resignation.cancellation_date) - elif pause.start_date > resignation.cancellation_date: - pause.delete() + if resignation.pay_out_day <= pause.end_date: + pause.end_date = resignation.pay_out_day + pause.save() else: - pause.update(end_date=resignation.cancellation_date) + if pause.start_date > resignation.pay_out_day: + pause.delete() + else: + pause.end_date = resignation.cancellation_date + pause.save() diff --git a/tapir/coop/templates/coop/draftuser_list.html b/tapir/coop/templates/coop/draftuser_list.html index 05bfce701..d4ffa763e 100644 --- a/tapir/coop/templates/coop/draftuser_list.html +++ b/tapir/coop/templates/coop/draftuser_list.html @@ -52,7 +52,7 @@