Skip to content

Commit c4631e4

Browse files
committed
Display reimbursement amounts in grants admin
Update GrantAdmin to show approved reimbursements and total allocated amount. Add utility methods to Grant model for checking reimbursement status. Key changes: - Add column for approved reimbursement categories and amounts - Add inline editor for GrantReimbursement - Add `total_allocated_amount` and `has_approved` helper methods - Optimize queries with select_related and prefetch_related
1 parent 007719e commit c4631e4

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

backend/grants/admin.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
create_addition_admin_log_entry,
55
create_change_admin_log_entry,
66
)
7+
from django.db.models import Value, IntegerField
8+
9+
from django.db.models import Sum
10+
from django.db.models.functions import Coalesce
711
from conferences.models.conference_voucher import ConferenceVoucher
812
from pycon.constants import UTC
913
from custom_admin.admin import (
@@ -402,6 +406,7 @@ def queryset(self, request, queryset):
402406
class GrantReimbursementCategoryAdmin(ConferencePermissionMixin, admin.ModelAdmin):
403407
list_display = ("__str__", "max_amount", "category", "included_by_default")
404408
list_filter = ("conference", "category", "included_by_default")
409+
search_fields = ("category", "name")
405410

406411

407412
@admin.register(GrantReimbursement)
@@ -416,23 +421,32 @@ class GrantReimbursementAdmin(ConferencePermissionMixin, admin.ModelAdmin):
416421
autocomplete_fields = ("grant",)
417422

418423

424+
class GrantReimbursementInline(admin.TabularInline):
425+
model = GrantReimbursement
426+
extra = 0
427+
autocomplete_fields = ["category"]
428+
fields = ["category", "granted_amount"]
429+
430+
419431
@admin.register(Grant)
420432
class GrantAdmin(ExportMixin, ConferencePermissionMixin, admin.ModelAdmin):
421433
change_list_template = "admin/grants/grant/change_list.html"
422434
resource_class = GrantResource
423435
list_display = (
424-
"user_display_name",
436+
"user",
425437
"country",
426438
"is_proposed_speaker",
427439
"is_confirmed_speaker",
428440
"emoji_gender",
429441
"conference",
430442
"status",
431443
"approved_type",
444+
"approved_amounts_display",
432445
"ticket_amount",
433446
"travel_amount",
434447
"accommodation_amount",
435448
"total_amount",
449+
"total_amount_display",
436450
"country_type",
437451
"user_has_ticket",
438452
"has_voucher",
@@ -472,6 +486,7 @@ class GrantAdmin(ExportMixin, ConferencePermissionMixin, admin.ModelAdmin):
472486
"delete_selected",
473487
]
474488
autocomplete_fields = ("user",)
489+
inlines = [GrantReimbursementInline]
475490

476491
fieldsets = (
477492
(
@@ -607,10 +622,22 @@ def user_has_ticket(self, obj: Grant) -> bool:
607622
def has_voucher(self, obj: Grant) -> bool:
608623
return obj.has_voucher
609624

625+
@admin.display(description="Total")
626+
def total_amount_display(self, obj):
627+
return f"{obj.total_allocated:.2f}"
628+
629+
@admin.display(description="Approved Reimbursements")
630+
def approved_amounts_display(self, obj):
631+
return ", ".join(
632+
f"{r.category.name}: {r.granted_amount}" for r in obj.reimbursements.all()
633+
)
634+
610635
def get_queryset(self, request):
611636
qs = (
612637
super()
613638
.get_queryset(request)
639+
.select_related("user")
640+
.prefetch_related("reimbursements__category")
614641
.annotate(
615642
is_proposed_speaker=Exists(
616643
Submission.objects.non_cancelled().filter(
@@ -631,6 +658,11 @@ def get_queryset(self, request):
631658
user_id=OuterRef("user_id"),
632659
)
633660
),
661+
total_allocated=Coalesce(
662+
Sum("reimbursements__granted_amount"),
663+
Value(0),
664+
output_field=IntegerField(),
665+
),
634666
)
635667
)
636668

backend/grants/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,13 @@ def has_approved_accommodation(self):
373373
or self.approved_type == Grant.ApprovedType.ticket_travel_accommodation
374374
)
375375

376+
@property
377+
def total_allocated_amount(self):
378+
return sum(r.allocated_amount for r in self.reimbursements.all())
379+
380+
def has_approved(self, type_):
381+
return self.reimbursements.filter(category__category=type_).exists()
382+
376383

377384
class GrantReimbursement(models.Model):
378385
"""Links a Grant to its reimbursement categories and stores the actual amount granted."""

0 commit comments

Comments
 (0)