Coverage for website/payments/tests/test_admin_views.py: 100.00%
319 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1from unittest import mock
2from unittest.mock import MagicMock, Mock, PropertyMock, patch
4from django.apps import apps
5from django.contrib.admin.utils import model_ngettext
6from django.contrib.auth.models import Permission
7from django.contrib.contenttypes.models import ContentType
8from django.test import Client, TestCase, override_settings
9from django.utils import timezone
10from django.utils.translation import gettext_lazy as _
12from freezegun import freeze_time
14from members.models import Member, Profile
15from payments import admin_views
16from payments.models import BankAccount, Batch, Payment, PaymentUser
17from payments.payables import payables
18from payments.tests.__mocks__ import MockModel
19from payments.tests.test_services import MockPayable
22@override_settings(SUSPEND_SIGNALS=True)
23class PaymentAdminViewTest(TestCase):
24 @classmethod
25 def setUpTestData(cls):
26 cls.user = Member.objects.create(
27 username="test1",
28 first_name="Test1",
29 last_name="Example",
30 email="test1@example.org",
31 )
32 Profile.objects.create(
33 user=cls.user,
34 )
35 cls.user = PaymentUser.objects.get(pk=cls.user.pk)
36 cls.payment = Payment.objects.create(
37 amount=7.5, processed_by=cls.user, paid_by=cls.user, type=Payment.CARD
38 )
40 def setUp(self):
41 payables.register(MockModel, MockPayable)
42 self.client = Client()
43 self.client.force_login(self.user)
44 self.view = admin_views.PaymentAdminView()
46 def tearDown(self):
47 payables._unregister(MockModel)
49 def _give_user_permissions(self):
50 content_type = ContentType.objects.get_for_model(Payment)
51 permissions = Permission.objects.filter(
52 content_type__app_label=content_type.app_label,
53 )
54 for p in permissions:
55 self.user.user_permissions.add(p)
56 self.user.is_staff = True
57 self.user.save()
59 self.client.logout()
60 self.client.force_login(self.user)
62 def test_redirect_without_permissions(self):
63 url = "/admin/payments/payment/app_label/model/pk/create/"
64 response = self.client.post(
65 url,
66 {
67 "type": "cash_payment",
68 },
69 )
70 self.assertEqual(response.status_code, 302)
71 self.assertURLEqual(response.url, f"/admin/login/?next={url}")
73 @mock.patch("django.contrib.messages.error")
74 @mock.patch("django.contrib.messages.success")
75 @mock.patch("django.shortcuts.resolve_url")
76 @mock.patch("payments.services.create_payment")
77 @mock.patch("payments.payables.payables.get_payable")
78 def test_post(
79 self, get_payable, create_payment, resolve_url, messages_success, messages_error
80 ):
81 url = "/admin/payments/payment/mock_label/model/pk/create/"
82 self._give_user_permissions()
83 payable = MockPayable(MockModel(payer=self.user))
85 original_get_model = apps.get_model
86 mock_get_model = MagicMock()
88 def side_effect(*args, **kwargs):
89 if "app_label" in kwargs and kwargs["app_label"] == "mock_label":
90 return mock_get_model
91 return original_get_model(*args, **kwargs)
93 apps.get_model = Mock(side_effect=side_effect)
94 get_payable.return_value = payable
95 create_payment.return_value = self.payment
96 resolve_url.return_value = "/resolved_url"
98 with self.subTest("Send post without payload"):
99 response = self.client.post(url)
101 self.assertEqual(response.status_code, 400)
103 create_payment.assert_not_called()
104 messages_error.assert_not_called()
105 messages_success.assert_not_called()
107 with self.subTest("Send post with successful processing, no next"):
108 payment_type = "cash_payment"
109 response = self.client.post(
110 url,
111 {
112 "type": payment_type,
113 },
114 )
116 self.assertEqual(response.status_code, 302)
117 self.assertEqual(response.url, "/resolved_url")
119 create_payment.assert_called_once_with(payable, self.user, payment_type)
120 resolve_url.assert_called_once_with(
121 "admin:payments_payment_change", self.payment.pk
122 )
124 messages_success.assert_called_once_with(
125 response.wsgi_request, "Successfully paid MockPayable."
126 )
128 create_payment.reset_mock()
129 messages_success.reset_mock()
130 resolve_url.reset_mock()
132 with self.subTest("Send post with successful processing and next"):
133 payment_type = "cash_payment"
134 response = self.client.post(
135 url,
136 {"type": payment_type, "next": "/admin/events/"},
137 )
139 self.assertEqual(response.status_code, 302)
140 self.assertEqual(response.url, "/resolved_url")
142 create_payment.assert_called_once_with(payable, self.user, payment_type)
143 resolve_url.assert_called_once_with("/admin/events/")
145 messages_success.assert_called_once_with(
146 response.wsgi_request,
147 "Successfully paid MockPayable.",
148 )
150 create_payment.reset_mock()
151 messages_success.reset_mock()
152 resolve_url.reset_mock()
154 with self.subTest("Send post with insecure next"):
155 response = self.client.post(
156 url,
157 {"type": "cash_payment", "next": "https://ru.nl/"},
158 )
160 self.assertEqual(response.status_code, 400)
162 create_payment.assert_not_called()
163 messages_error.assert_not_called()
164 messages_success.assert_not_called()
166 with self.subTest("Send post without permission to process the payment"):
167 payable.model.can_manage = False
168 response = self.client.post(
169 url,
170 {
171 "type": payment_type,
172 },
173 follow=True,
174 )
175 self.assertEqual(response.status_code, 403)
176 payable.model.can_manage = True
178 with self.subTest("Send post with failed processing"):
179 create_payment.return_value = None
180 response = self.client.post(
181 url,
182 {
183 "type": payment_type,
184 },
185 )
187 messages_error.assert_called_once_with(
188 response.wsgi_request,
189 "Could not pay MockPayable.",
190 )
192 with self.subTest("Send post with exception during processing"):
193 create_payment.side_effect = Exception("A test exception was thrown.")
194 response = self.client.post(
195 url,
196 {
197 "type": payment_type,
198 },
199 )
201 messages_error.assert_called_with(
202 response.wsgi_request,
203 "Something went wrong paying MockPayable: A test exception was thrown.",
204 )
207@override_settings(SUSPEND_SIGNALS=True, THALIA_PAY_ENABLED_PAYMENT_METHOD=True)
208@patch("payments.models.PaymentUser.tpay_allowed", PropertyMock, True)
209class BatchProcessAdminViewTest(TestCase):
210 fixtures = ["members.json", "bank_accounts.json"]
212 @classmethod
213 @patch("payments.models.PaymentUser.tpay_allowed", PropertyMock, True)
214 def setUpTestData(cls):
215 cls.batch = Batch.objects.create()
216 cls.user = PaymentUser.objects.get(pk=2)
217 Payment.objects.create(
218 amount=99,
219 paid_by=cls.user,
220 processed_by=cls.user,
221 batch=cls.batch,
222 type=Payment.TPAY,
223 )
225 def setUp(self):
226 self.client = Client()
227 self.client.force_login(self.user)
228 self.view = admin_views.PaymentAdminView()
230 def _give_user_permissions(self):
231 content_type = ContentType.objects.get_for_model(Batch)
232 permissions = Permission.objects.filter(
233 content_type__app_label=content_type.app_label,
234 )
235 for p in permissions:
236 self.user.user_permissions.add(p)
237 self.user.is_staff = True
238 self.user.save()
240 self.client.logout()
241 self.client.force_login(self.user)
243 def test_permissions(self):
244 url = f"/admin/payments/batch/{self.batch.id}/process/"
245 response = self.client.post(url)
246 self.assertEqual(response.status_code, 302)
247 self.assertEqual(response.url, f"/user/account/login/?next={url}")
249 self._give_user_permissions()
251 response = self.client.post(url, follow=True)
252 self.assertRedirects(response, f"/admin/payments/batch/{self.batch.id}/change/")
254 def test_next_validation(self):
255 self._give_user_permissions()
257 response = self.client.post(
258 f"/admin/payments/batch/{self.batch.id}/process/",
259 {"next": "https://google.com"},
260 )
262 self.assertEqual(response.status_code, 400)
264 @mock.patch("django.contrib.messages.error")
265 @mock.patch("django.contrib.messages.success")
266 def test_post(self, message_success, message_error):
267 self._give_user_permissions()
269 response = self.client.post(
270 f"/admin/payments/batch/{self.batch.id}/process/",
271 {"next": "/admin/events/"},
272 )
274 self.assertEqual(response.status_code, 302)
275 self.assertEqual(response.url, "/admin/events/")
277 self.assertEqual(Batch.objects.get(id=self.batch.id).processed, True)
279 message_success.assert_called_once_with(
280 response.wsgi_request,
281 _("Successfully processed {}.").format(model_ngettext(self.batch, 1)),
282 )
284 response = self.client.post(f"/admin/payments/batch/{self.batch.id}/process/")
286 self.assertEqual(Batch.objects.get(id=self.batch.id).processed, True)
288 message_error.assert_called_once_with(
289 response.wsgi_request,
290 _("{} already processed.").format(model_ngettext(self.batch, 1)),
291 )
294@override_settings(SUSPEND_SIGNALS=True)
295@freeze_time("2020-01-01")
296class BatchExportAdminViewTest(TestCase):
297 @classmethod
298 def setUpTestData(cls):
299 cls.batch = Batch.objects.create()
300 cls.user = Member.objects.create(
301 username="test1",
302 first_name="Test1",
303 last_name="Example",
304 email="test1@example.org",
305 )
306 Profile.objects.create(user=cls.user)
307 cls.user = PaymentUser.objects.get(pk=cls.user.pk)
309 def setUp(self):
310 self.client = Client()
311 self.client.force_login(self.user)
312 self.view = admin_views.BatchExportAdminView()
314 def _give_user_permissions(self):
315 content_type = ContentType.objects.get_for_model(Batch)
316 permissions = Permission.objects.filter(
317 content_type__app_label=content_type.app_label,
318 )
319 for p in permissions:
320 self.user.user_permissions.add(p)
321 self.user.is_staff = True
322 self.user.save()
324 self.client.logout()
325 self.client.force_login(self.user)
327 def test_permission(self):
328 url = f"/admin/payments/batch/{self.batch.id}/export/"
329 response = self.client.post(url)
330 self.assertEqual(response.status_code, 302)
331 self.assertURLEqual(response.url, f"/admin/login/?next={url}")
333 self._give_user_permissions()
335 response = self.client.post(url, follow=True)
336 self.assertEqual(response.status_code, 200)
338 def test_post(self):
339 self._give_user_permissions()
341 user2 = Member.objects.create(
342 username="test2",
343 first_name="Test2",
344 last_name="Example",
345 email="test1@example.org",
346 )
347 Profile.objects.create(user=user2)
348 user2 = PaymentUser.objects.get(pk=user2.pk)
350 BankAccount.objects.create(
351 last_used=timezone.now(),
352 owner=self.user,
353 iban="DE75512108001245126199",
354 mandate_no="2",
355 valid_from=timezone.now(),
356 initials="T.E.S.T.",
357 last_name="ersssss",
358 )
359 BankAccount.objects.create(
360 last_used=timezone.now(),
361 owner=user2,
362 iban="NL02ABNA0123456789",
363 mandate_no="1",
364 valid_from=timezone.now(),
365 initials="T.E.S.T.",
366 last_name="ersssss2",
367 )
369 Payment.objects.bulk_create(
370 [
371 Payment(
372 amount=1, paid_by=self.user, type=Payment.TPAY, batch=self.batch
373 ),
374 Payment(
375 amount=2, paid_by=self.user, type=Payment.TPAY, batch=self.batch
376 ),
377 Payment(amount=4, paid_by=user2, type=Payment.TPAY, batch=self.batch),
378 Payment(amount=2, paid_by=user2, type=Payment.TPAY, batch=self.batch),
379 ]
380 )
382 response = self.client.post(f"/admin/payments/batch/{self.batch.id}/export/")
384 self.assertEqual(
385 response.content,
386 b"Account holder,IBAN,Mandate Reference,Amount,Description,Mandate Date\r\n"
387 b"T.E.S.T. ersssss,DE75512108001245126199,2,3.00,Thalia Pay payments for 2020-1,2020-01-01\r\n"
388 b"T.E.S.T. ersssss2,NL02ABNA0123456789,1,6.00,Thalia Pay payments for 2020-1,2020-01-01\r\n",
389 )
392@override_settings(SUSPEND_SIGNALS=True)
393@freeze_time("2020-01-01")
394class BatchTopicExportAdminViewTest(TestCase):
395 @classmethod
396 def setUpTestData(cls):
397 cls.batch = Batch.objects.create()
398 cls.user = Member.objects.create(
399 username="test1",
400 first_name="Test1",
401 last_name="Example",
402 email="test1@example.org",
403 )
404 Profile.objects.create(user=cls.user)
405 cls.user = PaymentUser.objects.get(pk=cls.user.pk)
407 def setUp(self):
408 self.client = Client()
409 self.client.force_login(self.user)
410 self.view = admin_views.BatchTopicExportAdminView()
412 def _give_user_permissions(self):
413 content_type = ContentType.objects.get_for_model(Batch)
414 permissions = Permission.objects.filter(
415 content_type__app_label=content_type.app_label,
416 )
417 for p in permissions:
418 self.user.user_permissions.add(p)
419 self.user.is_staff = True
420 self.user.save()
422 self.client.logout()
423 self.client.force_login(self.user)
425 def test_permission(self):
426 url = f"/admin/payments/batch/{self.batch.id}/export-topic/"
427 response = self.client.post(url)
428 self.assertEqual(response.status_code, 302)
429 self.assertURLEqual(response.url, f"/admin/login/?next={url}")
431 self._give_user_permissions()
433 response = self.client.post(url)
434 self.assertEqual(response.status_code, 200)
436 def test_post(self):
437 self._give_user_permissions()
439 user2 = Member.objects.create(
440 username="test2",
441 first_name="Test2",
442 last_name="Example",
443 email="test1@example.org",
444 )
445 Profile.objects.create(user=user2)
446 user2 = PaymentUser.objects.get(pk=user2.pk)
448 BankAccount.objects.create(
449 last_used=timezone.now(),
450 owner=self.user,
451 iban="DE75512108001245126199",
452 mandate_no="2",
453 valid_from=timezone.now(),
454 )
455 BankAccount.objects.create(
456 last_used=timezone.now(),
457 owner=user2,
458 iban="NL02ABNA0123456789",
459 mandate_no="1",
460 valid_from=timezone.now(),
461 )
463 Payment.objects.bulk_create(
464 [
465 Payment(
466 amount=1,
467 paid_by=self.user,
468 type=Payment.TPAY,
469 batch=self.batch,
470 topic="test1",
471 ),
472 Payment(
473 amount=2,
474 paid_by=self.user,
475 type=Payment.TPAY,
476 batch=self.batch,
477 topic="test2",
478 ),
479 Payment(
480 amount=4,
481 paid_by=user2,
482 type=Payment.TPAY,
483 batch=self.batch,
484 topic="test1",
485 ),
486 Payment(
487 amount=2,
488 paid_by=user2,
489 type=Payment.TPAY,
490 batch=self.batch,
491 topic="test2",
492 ),
493 ]
494 )
496 response = self.client.post(
497 f"/admin/payments/batch/{self.batch.id}/export-topic/"
498 )
500 self.assertEqual(
501 response.content,
502 b"Topic,No. of payments,First payment,Last payment,Total amount\r\n"
503 b"test1,2,2020-01-01,2020-01-01,5.00\r\n"
504 b"test2,2,2020-01-01,2020-01-01,4.00\r\n",
505 )
508@override_settings(SUSPEND_SIGNALS=True)
509@freeze_time("2020-01-01")
510class BatchTopicDescriptionAdminViewTest(TestCase):
511 @classmethod
512 def setUpTestData(cls):
513 cls.batch = Batch.objects.create()
514 cls.user = Member.objects.create(
515 username="test1",
516 first_name="Test1",
517 last_name="Example",
518 email="test1@example.org",
519 )
520 Profile.objects.create(user=cls.user)
522 def setUp(self):
523 self.client = Client()
524 self.client.force_login(self.user)
525 self.view = admin_views.BatchTopicDescriptionAdminView()
527 def _give_user_permissions(self):
528 content_type = ContentType.objects.get_for_model(Batch)
529 permissions = Permission.objects.filter(
530 content_type__app_label=content_type.app_label,
531 )
532 for p in permissions:
533 self.user.user_permissions.add(p)
534 self.user.is_staff = True
535 self.user.save()
537 self.client.logout()
538 self.client.force_login(self.user)
540 def test_permission(self):
541 url = f"/admin/payments/batch/{self.batch.id}/topic-description/"
542 response = self.client.post(url)
543 self.assertEqual(response.status_code, 302)
544 self.assertURLEqual(response.url, f"/admin/login/?next={url}")
546 self._give_user_permissions()
548 response = self.client.post(url)
549 self.assertEqual(response.status_code, 200)
551 def test_post(self):
552 self._give_user_permissions()
554 self.user2 = Member.objects.create(
555 username="test2",
556 first_name="Test2",
557 last_name="Example",
558 email="test1@example.org",
559 )
560 Profile.objects.create(user=self.user2)
562 BankAccount.objects.create(
563 last_used=timezone.now(),
564 owner=self.user,
565 iban="DE75512108001245126199",
566 mandate_no="2",
567 valid_from=timezone.now(),
568 )
569 BankAccount.objects.create(
570 last_used=timezone.now(),
571 owner=self.user2,
572 iban="NL02ABNA0123456789",
573 mandate_no="1",
574 valid_from=timezone.now(),
575 )
577 Payment.objects.bulk_create(
578 [
579 Payment(
580 amount=1,
581 paid_by=self.user,
582 type=Payment.TPAY,
583 batch=self.batch,
584 topic="test1",
585 ),
586 Payment(
587 amount=2,
588 paid_by=self.user,
589 type=Payment.TPAY,
590 batch=self.batch,
591 topic="test2",
592 ),
593 Payment(
594 amount=4,
595 paid_by=self.user2,
596 type=Payment.TPAY,
597 batch=self.batch,
598 topic="test1",
599 ),
600 Payment(
601 amount=2,
602 paid_by=self.user2,
603 type=Payment.TPAY,
604 batch=self.batch,
605 topic="test2",
606 ),
607 ]
608 )
610 response = self.client.post(
611 f"/admin/payments/batch/{self.batch.id}/topic-description/"
612 )
614 self.assertContains(
615 response,
616 "- test1 (2x) [2020-01-01 -- 2020-01-01], total €5.00\n- test2 (2x) [2020-01-01 -- 2020-01-01], total €4.00\n",
617 )
620@override_settings(SUSPEND_SIGNALS=True)
621class BatchNewFilledAdminViewTest(TestCase):
622 @classmethod
623 def setUpTestData(cls):
624 cls.batch = Batch.objects.create()
625 cls.user = Member.objects.create(
626 username="test1",
627 first_name="Test1",
628 last_name="Example",
629 email="test1@example.org",
630 )
631 Profile.objects.create(user=cls.user)
632 cls.user = PaymentUser.objects.get(pk=cls.user.pk)
634 def setUp(self):
635 self.client = Client()
636 self.client.force_login(self.user)
637 self.view = admin_views.BatchExportAdminView()
639 def _give_user_permissions(self):
640 content_type = ContentType.objects.get_for_model(Batch)
641 permissions = Permission.objects.filter(
642 content_type__app_label=content_type.app_label,
643 )
644 for p in permissions:
645 self.user.user_permissions.add(p)
646 self.user.is_staff = True
647 self.user.save()
649 self.client.logout()
650 self.client.force_login(self.user)
652 def test_permission(self):
653 url = "/admin/payments/batch/new_filled/"
654 response = self.client.post(url)
655 self.assertEqual(response.status_code, 302)
656 self.assertURLEqual(response.url, f"/admin/login/?next={url}")
658 self._give_user_permissions()
660 response = self.client.post(url)
662 b = Batch.objects.exclude(id=self.batch.id).first()
663 self.assertRedirects(response, f"/admin/payments/batch/{b.id}/change/")
665 @freeze_time("2020-03-01")
666 def test_post(self):
667 self._give_user_permissions()
669 Payment.objects.bulk_create(
670 [
671 Payment(
672 amount=1,
673 type=Payment.TPAY,
674 created_at=timezone.datetime(
675 2020, 1, 31, tzinfo=timezone.timezone.utc
676 ),
677 ),
678 Payment(
679 amount=2,
680 type=Payment.TPAY,
681 created_at=timezone.datetime(
682 2020, 2, 1, tzinfo=timezone.timezone.utc
683 ),
684 ),
685 Payment(
686 amount=3,
687 type=Payment.TPAY,
688 created_at=timezone.datetime(
689 2020, 2, 10, tzinfo=timezone.timezone.utc
690 ),
691 batch=self.batch,
692 ),
693 Payment(
694 amount=4,
695 type=Payment.TPAY,
696 created_at=timezone.datetime(
697 2020, 2, 28, tzinfo=timezone.timezone.utc
698 ),
699 ),
700 Payment(
701 amount=5,
702 type=Payment.TPAY,
703 created_at=timezone.datetime(
704 2020, 2, 29, tzinfo=timezone.timezone.utc
705 ),
706 ),
707 Payment(
708 amount=6,
709 type=Payment.TPAY,
710 created_at=timezone.datetime(
711 2020, 3, 1, tzinfo=timezone.timezone.utc
712 ),
713 ),
714 Payment(
715 amount=7,
716 type=Payment.WIRE,
717 created_at=timezone.datetime(
718 2020, 1, 1, tzinfo=timezone.timezone.utc
719 ),
720 ),
721 ]
722 )
724 self.client.post("/admin/payments/batch/new_filled/")
726 b = Batch.objects.exclude(id=self.batch.id).first()
728 self.assertEqual(Payment.objects.get(amount=1).batch.id, b.id)
729 self.assertEqual(Payment.objects.get(amount=2).batch.id, b.id)
730 self.assertEqual(Payment.objects.get(amount=3).batch.id, self.batch.id)
731 self.assertEqual(Payment.objects.get(amount=4).batch.id, b.id)
732 self.assertEqual(Payment.objects.get(amount=5).batch.id, b.id)
733 self.assertEqual(Payment.objects.get(amount=6).batch.id, b.id)
734 self.assertIsNone(Payment.objects.get(amount=7).batch)