diff --git a/backend/custom_admin/admin.py b/backend/custom_admin/admin.py index 7151a2d84e..a1fe84fb7c 100644 --- a/backend/custom_admin/admin.py +++ b/backend/custom_admin/admin.py @@ -60,6 +60,7 @@ def wrapper(modeladmin, request, queryset): def confirm_pending_status(modeladmin, request, queryset): queryset.update( status=F("pending_status"), + pending_status=None, ) @@ -67,5 +68,5 @@ def confirm_pending_status(modeladmin, request, queryset): @validate_single_conference_selection def reset_pending_status_back_to_status(modeladmin, request, queryset): queryset.update( - pending_status=F("status"), + pending_status=None, ) diff --git a/backend/grants/admin.py b/backend/grants/admin.py index 6615e8bd06..bca135cd9e 100644 --- a/backend/grants/admin.py +++ b/backend/grants/admin.py @@ -652,8 +652,8 @@ def get_queryset(self, request): return ( super() .get_queryset(request) - .exclude( - pending_status=F("status"), + .filter( + pending_status__isnull=False, ) ) diff --git a/backend/grants/migrations/0029_alter_grant_pending_status.py b/backend/grants/migrations/0029_alter_grant_pending_status.py new file mode 100644 index 0000000000..d04d003dfb --- /dev/null +++ b/backend/grants/migrations/0029_alter_grant_pending_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.4 on 2025-07-27 14:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('grants', '0028_remove_grant_pretix_voucher_id_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='grant', + name='pending_status', + field=models.CharField(blank=True, choices=[('pending', 'Pending'), ('rejected', 'Rejected'), ('approved', 'Approved'), ('waiting_list', 'Waiting List'), ('waiting_list_maybe', 'Waiting List, Maybe'), ('waiting_for_confirmation', 'Waiting for confirmation'), ('refused', 'Refused'), ('confirmed', 'Confirmed'), ('did_not_attend', 'Did Not Attend')], max_length=30, null=True, verbose_name='pending status'), + ), + ] diff --git a/backend/grants/models.py b/backend/grants/models.py index a7bb447af8..d57384d550 100644 --- a/backend/grants/models.py +++ b/backend/grants/models.py @@ -149,7 +149,7 @@ class ApprovedType(models.TextChoices): _("pending status"), choices=Status.choices, max_length=30, - default=Status.pending, + null=True, blank=True, ) approved_type = models.CharField( @@ -229,9 +229,6 @@ def save(self, *args, **kwargs): self._update_country_type() self._calculate_grant_amounts() - if self.pending_status == self._original_status: - self.pending_status = self.status - update_fields = kwargs.get("update_fields", None) if update_fields: update_fields.append("total_amount") @@ -249,7 +246,7 @@ def save(self, *args, **kwargs): self._original_status = self.status def _calculate_grant_amounts(self): - if self.pending_status != Grant.Status.approved: + if self.current_or_pending_status != Grant.Status.approved: return if ( @@ -332,6 +329,10 @@ def has_approved_accommodation(self): or self.approved_type == Grant.ApprovedType.ticket_travel_accommodation ) + @property + def current_or_pending_status(self): + return self.pending_status or self.status + class GrantConfirmPendingStatusProxy(Grant): class Meta: diff --git a/backend/grants/tests/test_admin.py b/backend/grants/tests/test_admin.py index 9cf22c7ce8..d394a1eff9 100644 --- a/backend/grants/tests/test_admin.py +++ b/backend/grants/tests/test_admin.py @@ -485,6 +485,11 @@ def test_confirm_pending_status_action(rf): assert grant_1.status == Grant.Status.confirmed assert grant_2.status == Grant.Status.waiting_list assert grant_3.status == Grant.Status.waiting_list_maybe + + assert grant_1.pending_status is None + assert grant_2.pending_status is None + assert grant_3.pending_status is None + # Left out from the action assert grant_4.status == Grant.Status.waiting_list_maybe @@ -524,13 +529,13 @@ def test_reset_pending_status_back_to_status_action(rf): grant_4.refresh_from_db() assert grant_1.status == Grant.Status.pending - assert grant_1.pending_status == Grant.Status.pending + assert grant_1.pending_status is None assert grant_2.status == Grant.Status.rejected - assert grant_2.pending_status == Grant.Status.rejected + assert grant_2.pending_status is None assert grant_3.status == Grant.Status.waiting_list - assert grant_3.pending_status == Grant.Status.waiting_list + assert grant_3.pending_status is None # Left out from the action assert grant_4.status == Grant.Status.waiting_list_maybe diff --git a/backend/grants/tests/test_models.py b/backend/grants/tests/test_models.py index ce68b0479a..5bb922cb4f 100644 --- a/backend/grants/tests/test_models.py +++ b/backend/grants/tests/test_models.py @@ -162,7 +162,7 @@ def test_sets_country_type_does_nothing_if_unset(): assert grant.country_type is None -def test_syncs_pending_status_on_change(): +def test_pending_status_no_longer_syncs_with_status(): grant = GrantFactory( pending_status=Grant.Status.pending, status=Grant.Status.pending, @@ -171,10 +171,10 @@ def test_syncs_pending_status_on_change(): grant.status = Grant.Status.approved grant.save(update_fields=["status"]) - # Pending status should be updated to match the status + # Pending status should remain unchanged when status changes grant.refresh_from_db() - assert grant.pending_status == Grant.Status.approved + assert grant.pending_status == Grant.Status.pending # Should remain unchanged assert grant.status == Grant.Status.approved @@ -192,3 +192,34 @@ def test_doesnt_sync_pending_status_if_different_values(): assert grant.pending_status == Grant.Status.refused assert grant.status == Grant.Status.waiting_for_confirmation + + +def test_pending_status_none_means_no_pending_change(): + grant = GrantFactory( + pending_status=None, + status=Grant.Status.approved, + ) + + # When pending_status is None, the effective status should be the current status + # This affects the _calculate_grant_amounts method + grant.approved_type = Grant.ApprovedType.ticket_only + grant.departure_country = "IT" + grant.save() + + # Since effective status is approved (from status field), amounts should be calculated + assert grant.ticket_amount is not None + + +def test_pending_status_set_overrides_current_status(): + grant = GrantFactory( + pending_status=Grant.Status.approved, + status=Grant.Status.pending, + ) + + # When pending_status is set, it should be used as the effective status + grant.approved_type = Grant.ApprovedType.ticket_only + grant.departure_country = "IT" + grant.save() + + # Since effective status is approved (from pending_status), amounts should be calculated + assert grant.ticket_amount is not None diff --git a/backend/reviews/admin.py b/backend/reviews/admin.py index fd6c4eca92..4ec1e5af6d 100644 --- a/backend/reviews/admin.py +++ b/backend/reviews/admin.py @@ -160,7 +160,7 @@ def go_to_review_screen(self, obj): return mark_safe( f""" - + Go to review screen """ @@ -176,7 +176,7 @@ def go_to_recap_screen(self, obj): return mark_safe( f""" - + Go to recap screen """ @@ -282,7 +282,11 @@ def _review_grants_recap_view(self, request, review_session): approved_type = approved_type_decisions.get(grant.id, "") - grant.pending_status = decision + if decision != grant.status: + grant.pending_status = decision + elif decision == grant.status: + grant.pending_status = None + grant.approved_type = ( approved_type if decision == Grant.Status.approved else None ) @@ -736,7 +740,7 @@ def _render_proposal_review( seen=request.GET.get("seen", "").split(","), existing_comment=existing_comment, review_session_repr=str(review_session), - title=f'Proposal Review: {proposal.title.localize("en")}', + title=f"Proposal Review: {proposal.title.localize('en')}", ) return TemplateResponse(request, "proposal-review.html", context) diff --git a/backend/reviews/templates/grants-recap.html b/backend/reviews/templates/grants-recap.html index 967ca0074d..d41ddfb107 100644 --- a/backend/reviews/templates/grants-recap.html +++ b/backend/reviews/templates/grants-recap.html @@ -254,7 +254,7 @@ updateBottomBarUI(); }); - waitinglistInput.addEventListener("change", () => { + waitinglistmaybeInput.addEventListener("change", () => { grantData.status = "waiting_list_maybe"; updateBottomBarUI(); }); @@ -469,13 +469,13 @@