diff --git a/src/backend/core/admin.py b/src/backend/core/admin.py index d2034c67..b9964de2 100644 --- a/src/backend/core/admin.py +++ b/src/backend/core/admin.py @@ -2,6 +2,7 @@ from django.contrib import admin, messages from django.contrib.auth import admin as auth_admin +from django.db import transaction from django.shortcuts import redirect from django.utils.translation import gettext_lazy as _ @@ -119,39 +120,13 @@ def process_reconciliation(_modeladmin, _request, queryset): Admin action to process selected user reconciliations. The action will process only entries that are ready and have both emails checked. - Its action is threefold: - - Transfer document accesses from inactive to active user, updating roles as needed. - - Activate the active user and deactivate the inactive user. """ processable_entries = queryset.filter( status="ready", active_email_checked=True, inactive_email_checked=True ) - # Prepare the bulk operations - updated_documentaccess = [] - removed_documentaccess = [] - update_users_active_status = [] - for entry in processable_entries: - new_updated_documentaccess, new_removed_documentaccess = ( - entry.process_documentaccess_reconciliation() - ) - updated_documentaccess += new_updated_documentaccess - removed_documentaccess += new_removed_documentaccess - - entry.active_user.is_active = True - entry.inactive_user.is_active = False - update_users_active_status.append(entry.active_user) - update_users_active_status.append(entry.inactive_user) - - # Actually perform the bulk operations - models.DocumentAccess.objects.bulk_update(updated_documentaccess, ["user", "role"]) - - if removed_documentaccess: - ids_to_delete = [rd.id for rd in removed_documentaccess] - models.DocumentAccess.objects.filter(id__in=ids_to_delete).delete() - - models.User.objects.bulk_update(update_users_active_status, ["is_active"]) + entry.process_reconciliation_request() @admin.register(models.UserReconciliation) diff --git a/src/backend/core/models.py b/src/backend/core/models.py index 31ff11ec..1a465a52 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -378,9 +378,44 @@ class UserReconciliation(BaseModel): super().save(*args, **kwargs) - def process_documentaccess_reconciliation(self): + @transaction.atomic + def process_reconciliation_request(self): """ - Process the reconciliation by transferring document accesses from the inactive user + Process the reconciliation request as a transaction. + + Its action is threefold: + - Transfer document accesses from inactive to active user, updating roles as needed. + - Activate the active user and deactivate the inactive user. + - Update the reconciliation entry itself. + """ + + # Prepare the data to perform the reconciliation on + updated_accesses, removed_accesses = ( + self.prepare_documentaccess_reconciliation() + ) + self.active_user.is_active = True + self.inactive_user.is_active = False + + # Actually perform the bulk operations + DocumentAccess.objects.bulk_update(updated_accesses, ["user", "role"]) + + if removed_accesses: + ids_to_delete = [rd.id for rd in removed_accesses] + DocumentAccess.objects.filter(id__in=ids_to_delete).delete() + + User.objects.bulk_update([self.active_user, self.inactive_user], ["is_active"]) + + # Wrap up the reconciliation entry + self.logs += f"""Requested update for {len(updated_accesses)} DocumentAccess items + and deletion for {len(removed_accesses)} DocumentAccess items.\n""" + self.status = "done" + self.save() + + self.send_reconciliation_done_email() + + def prepare_documentaccess_reconciliation(self): + """ + Prepare the reconciliation by transferring document accesses from the inactive user to the active user. """ updated_accesses = [] @@ -408,13 +443,6 @@ class UserReconciliation(BaseModel): entry.user = self.active_user updated_accesses.append(entry) - self.logs += f"""Requested update for {len(updated_accesses)} DocumentAccess items - and deletion for {len(removed_accesses)} DocumentAccess items.\n""" - self.status = "done" - self.send_reconciliation_done_email() - - self.save() - return updated_accesses, removed_accesses def send_reconciliation_confirm_email(