Show paid status in admin and filter CSV exports by year (#30)
## Summary Closes #30. - Green check on membership/person admin when a current-year payment email matches - Year picker on membership and payment CSV downloads - Payments admin search by email, plus year list filters
This commit is contained in:
@@ -6,12 +6,17 @@ from django.urls import reverse
|
||||
|
||||
from scha.settings.base import build_csrf_trusted_origins
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils import timezone
|
||||
|
||||
from .admin import membership_payment_exists, person_payment_exists
|
||||
from .forms import AddressForm, PeopleForm
|
||||
from .models import (
|
||||
AddressModel1,
|
||||
CalendarEvent,
|
||||
Membership,
|
||||
MembershipPerson,
|
||||
Payments,
|
||||
SCHAOfficer,
|
||||
UsefulLinks,
|
||||
)
|
||||
@@ -469,3 +474,183 @@ class AuthViewTests(TestCase):
|
||||
response = self.views.member_posts(request)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn("/login", response.url)
|
||||
|
||||
|
||||
def _membership_with_person(email="alex@example.com"):
|
||||
membership = Membership.objects.create()
|
||||
AddressModel1.objects.create(
|
||||
membership=membership,
|
||||
address_1="123 Main St",
|
||||
city="Wheaton",
|
||||
state="IL",
|
||||
zip_code="60189",
|
||||
)
|
||||
person = MembershipPerson.objects.create(
|
||||
membership=membership,
|
||||
first_name="Alex",
|
||||
last_name="Resident",
|
||||
email=email,
|
||||
phone_number="+16305559876",
|
||||
)
|
||||
return membership, person
|
||||
|
||||
|
||||
class AdminPaidIndicatorTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.admin_user = User.objects.create_superuser(
|
||||
"admin", "admin@example.com", "pass"
|
||||
)
|
||||
self.client.force_login(self.admin_user)
|
||||
self.this_year = timezone.now().year
|
||||
self.membership, self.person = _membership_with_person("paid@example.com")
|
||||
|
||||
def test_person_marked_paid_when_email_matches_current_year_payment(self):
|
||||
Payments.objects.create(
|
||||
email="PAID@example.com",
|
||||
date=datetime.date(self.this_year, 3, 1),
|
||||
)
|
||||
|
||||
paid = MembershipPerson.objects.annotate(
|
||||
_has_payment=person_payment_exists()
|
||||
).get(pk=self.person.pk)
|
||||
self.assertTrue(paid._has_payment)
|
||||
|
||||
unpaid_membership, unpaid_person = _membership_with_person("unpaid@example.com")
|
||||
unpaid = MembershipPerson.objects.annotate(
|
||||
_has_payment=person_payment_exists()
|
||||
).get(pk=unpaid_person.pk)
|
||||
self.assertFalse(unpaid._has_payment)
|
||||
|
||||
membership = Membership.objects.annotate(
|
||||
_has_payment=membership_payment_exists()
|
||||
).get(pk=self.membership.pk)
|
||||
self.assertTrue(membership._has_payment)
|
||||
other = Membership.objects.annotate(
|
||||
_has_payment=membership_payment_exists()
|
||||
).get(pk=unpaid_membership.pk)
|
||||
self.assertFalse(other._has_payment)
|
||||
|
||||
def test_prior_year_payment_does_not_count_as_paid(self):
|
||||
Payments.objects.create(
|
||||
email="paid@example.com",
|
||||
date=datetime.date(self.this_year - 1, 6, 1),
|
||||
)
|
||||
person = MembershipPerson.objects.annotate(
|
||||
_has_payment=person_payment_exists()
|
||||
).get(pk=self.person.pk)
|
||||
self.assertFalse(person._has_payment)
|
||||
|
||||
def test_person_changelist_shows_paid_icon(self):
|
||||
Payments.objects.create(
|
||||
email="paid@example.com",
|
||||
date=datetime.date(self.this_year, 4, 15),
|
||||
)
|
||||
url = reverse("admin:schasite_membershipperson_changelist")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Paid this year")
|
||||
self.assertContains(response, "icon-yes.svg")
|
||||
|
||||
|
||||
class AdminPaymentSearchTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.admin_user = User.objects.create_superuser(
|
||||
"admin", "admin@example.com", "pass"
|
||||
)
|
||||
self.client.force_login(self.admin_user)
|
||||
Payments.objects.create(email="dues@example.com", date=datetime.date(2026, 1, 10))
|
||||
Payments.objects.create(email="other@example.com", date=datetime.date(2026, 2, 10))
|
||||
|
||||
def test_payments_admin_search_by_email(self):
|
||||
url = reverse("admin:schasite_payments_changelist")
|
||||
response = self.client.get(url, {"q": "dues@example.com"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "dues@example.com")
|
||||
self.assertNotContains(response, "other@example.com")
|
||||
|
||||
|
||||
class AdminCsvYearFilterTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.admin_user = User.objects.create_superuser(
|
||||
"admin", "admin@example.com", "pass"
|
||||
)
|
||||
self.client.force_login(self.admin_user)
|
||||
|
||||
self.current, self.current_person = _membership_with_person("now@example.com")
|
||||
self.old, self.old_person = _membership_with_person("old@example.com")
|
||||
Membership.objects.filter(pk=self.old.pk).update(
|
||||
created=timezone.make_aware(datetime.datetime(2024, 5, 1, 12, 0, 0))
|
||||
)
|
||||
|
||||
Payments.objects.create(email="now@example.com", date=datetime.date(2026, 3, 1))
|
||||
Payments.objects.create(email="old@example.com", date=datetime.date(2024, 3, 1))
|
||||
|
||||
def test_membership_csv_prompts_for_year(self):
|
||||
url = reverse("admin:schasite_membership_changelist")
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"action": "download_csv_by_members",
|
||||
"_selected_action": [str(self.current.pk), str(self.old.pk)],
|
||||
"index": "0",
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Year:")
|
||||
self.assertContains(response, "Download CSV")
|
||||
|
||||
def test_membership_csv_filters_by_created_year(self):
|
||||
url = reverse("admin:schasite_membership_changelist")
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"action": "download_csv_by_members",
|
||||
"_selected_action": [str(self.current.pk), str(self.old.pk)],
|
||||
"index": "0",
|
||||
"apply": "Download CSV",
|
||||
"year": "2024",
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response["Content-Type"], "text/csv")
|
||||
content = response.content.decode()
|
||||
self.assertIn("old@example.com", content)
|
||||
self.assertNotIn("now@example.com", content)
|
||||
|
||||
def test_membership_csv_by_address_filters_by_created_year(self):
|
||||
url = reverse("admin:schasite_membership_changelist")
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"action": "download_csv_by_address",
|
||||
"_selected_action": [str(self.current.pk), str(self.old.pk)],
|
||||
"index": "0",
|
||||
"apply": "Download CSV",
|
||||
"year": "2024",
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
content = response.content.decode()
|
||||
self.assertIn("old@example.com", content)
|
||||
self.assertNotIn("now@example.com", content)
|
||||
|
||||
def test_payments_csv_filters_by_payment_year(self):
|
||||
url = reverse("admin:schasite_payments_changelist")
|
||||
payments = list(Payments.objects.values_list("pk", flat=True))
|
||||
response = self.client.post(
|
||||
url,
|
||||
{
|
||||
"action": "download_payments",
|
||||
"_selected_action": [str(pk) for pk in payments],
|
||||
"index": "0",
|
||||
"apply": "Download CSV",
|
||||
"year": "2024",
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
content = response.content.decode()
|
||||
self.assertIn("old@example.com", content)
|
||||
self.assertNotIn("now@example.com", content)
|
||||
|
||||
Reference in New Issue
Block a user