-
Notifications
You must be signed in to change notification settings - Fork 22
Flexible Grant categories #4420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4420 +/- ##
==========================================
- Coverage 94.53% 92.71% -1.82%
==========================================
Files 352 352
Lines 10131 10123 -8
Branches 733 725 -8
==========================================
- Hits 9577 9386 -191
- Misses 461 645 +184
+ Partials 93 92 -1 🚀 New features to boost your workflow:
|
c4631e4 to
2158bf3
Compare
2158bf3 to
a9233db
Compare
Remove hardcoded default grant amounts for ticket, accommodation, and travel from `Conference` in favor of using `GrantReimbursementCategory`. Update all relevant admin forms, models, and templates to reference flexible categories instead of fixed fields. - Remove legacy fields: `grants_default_ticket_amount`, `grants_default_accommodation_amount`, `grants_default_travel_from_italy_amount`, and `grants_default_travel_from_europe_amount` from `Conference` - Update `Grant` and `GrantReimbursement` logic to work exclusively with `GrantReimbursementCategory` - Refactor grant review admin and summary logic to support multiple, configurable reimbursement categories per grant - Migrate existing grants to new reimbursement category scheme - Add and update tests and migrations to cover flexible grant categories This change allows flexible reimbursement types (and amounts) to be configured per conference, supports granular grant allocation, and paves the way for internationalization and more complex business rules.
a9233db to
7c8820c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR refactors the grant reimbursement system from a fixed set of approved types to a flexible category-based approach. The changes enable conference organizers to define custom reimbursement categories (e.g., Travel, Ticket, Accommodation) with individual maximum amounts, replacing the previous hardcoded approved_type field.
Key changes:
- Introduced
GrantReimbursementCategoryandGrantReimbursementmodels for flexible grant management - Migrated existing grant data from the old
approved_typesystem to the new category-based system - Updated admin interfaces to display and manage reimbursements through the new models
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/grants/models.py | Added new models for reimbursement categories and reimbursements; removed deprecated fields and calculation logic |
| backend/grants/admin.py | Added admin interfaces for new models and updated grant display to show reimbursement details |
| backend/reviews/admin.py | Updated grant review workflow to handle multiple reimbursement categories instead of single approved type |
| backend/reviews/templates/grants-recap.html | Changed UI from radio buttons to checkboxes for selecting multiple reimbursement categories |
| backend/grants/summary.py | Refactored financial aggregation to sum from reimbursements instead of grant amounts |
| backend/grants/migrations/0030_remove_grant_accommodation_amount_and_more.py | Migration to create new models and backfill existing data |
| backend/conferences/migrations/0055_remove_conference_grants_default_accommodation_amount_and_more.py | Migration to remove deprecated conference-level grant amount fields |
| backend/conferences/models/conference.py | Removed deprecated grant default amount fields |
| backend/conferences/admin/conference.py | Removed deprecated grant configuration fieldset from admin |
| backend/grants/tests/test_models.py | Updated tests to use new reimbursement system |
| backend/grants/tests/test_migration_backfill_grant_reimbursements.py | Added comprehensive migration tests |
| backend/reviews/tests/test_admin.py | Added tests for grant review workflow with new reimbursement system |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| """ | ||
| Aggregates financial data (total amounts) by grant status. | ||
| """ | ||
| financial_data = filtered_grants.values("pending_status").annotate( | ||
| total_amount_sum=Sum("total_amount") | ||
| ) | ||
| financial_summary = {status[0]: 0 for status in statuses} | ||
| overall_total = 0 | ||
|
|
||
| for data in financial_data: | ||
| pending_status = data["pending_status"] | ||
| total_amount = data["total_amount_sum"] or 0 | ||
| financial_summary[pending_status] += total_amount | ||
| if pending_status in self.BUDGET_STATUSES: | ||
| overall_total += total_amount | ||
| for status in statuses: | ||
| grants_for_status = filtered_grants.filter(pending_status=status[0]) | ||
| reimbursements = GrantReimbursement.objects.filter( | ||
| grant__in=grants_for_status | ||
| ) | ||
| total = reimbursements.aggregate(total=Sum("granted_amount"))["total"] or 0 | ||
| financial_summary[status[0]] = total | ||
| if status[0] in self.BUDGET_STATUSES: | ||
| overall_total += total | ||
|
|
||
| return financial_summary, overall_total |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This unused function should be removed since it has been replaced by _aggregate_financial_data_by_status_new (which should be renamed to take its place). Having both functions could cause confusion about which one is actually being used.
|
|
||
| @admin.display(description="Status") | ||
| def current_or_pending_status(self, obj): | ||
| return obj.current_or_pending_status |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Missing blank line after the method definition. There should be a blank line before the next method definition for consistency with Python style guidelines.
| return "" | ||
| @admin.display(description="Total") | ||
| def total_amount_display(self, obj): | ||
| return f"{obj.total_allocated:.2f}" |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The property name is total_allocated_amount (line 271 in models.py) but this code references obj.total_allocated which is the annotation added in the queryset. This should reference obj.total_allocated_amount to be consistent with the model property, or the comment should clarify this relies on the queryset annotation.
| "name": "Ticket", | ||
| "description": "Conference ticket", | ||
| "max_amount": conference.grants_default_ticket_amount | ||
| or Decimal("0.00"), |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing import for Decimal. The migration uses Decimal on lines 20, 31, and 42 but doesn't import it from the decimal module.
| }, | ||
| ), | ||
| # Backfill existing grants | ||
| migrations.RunPython( |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The migrate_grants function should be called before ensure_categories_exist to ensure categories exist before attempting to create reimbursements. Add a RunPython operation for ensure_categories_exist before this one.
| migrations.RunPython( | |
| migrations.RunPython( | |
| code=ensure_categories_exist, | |
| ), | |
| migrations.RunPython( |
| # TODO: move the amount calculation in a separate function maybe? | ||
| grant.save(update_fields=["pending_status", "approved_type"]) | ||
| grant.save(update_fields=["pending_status",]) | ||
| approved_reimbursement_categories = approved_reimbursement_categories_decisions.get(grant.id, "") |
Copilot
AI
Nov 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default value should be an empty list [] instead of an empty string "" to match the expected type. This is used in a for loop on line 318, and iterating over a string would produce unexpected behavior.
| approved_reimbursement_categories = approved_reimbursement_categories_decisions.get(grant.id, "") | |
| approved_reimbursement_categories = approved_reimbursement_categories_decisions.get(grant.id, []) |
What
ToDo