Repository: svthalia/concrexit

Issue:
Settled filter on payments API
### Is your feature request related to a problem? Please describe.
I'm always frustrated when i can't get the settled status of the payment from the API.

### Describe the solution you'd like
For Reaxit I want to filter the list of the user's payments by the if they are settled or not. The API does currently not provide this.

### Motivation
See above



Discussion:

Resolving patch:
diff --git a/website/payments/api/v2/filters.py b/website/payments/api/v2/filters.py
--- a/website/payments/api/v2/filters.py
+++ b/website/payments/api/v2/filters.py
@@ -1,6 +1,6 @@
 from rest_framework import filters
 
-from utils.snippets import extract_date_range
+from utils.snippets import extract_date_range, strtobool
 
 
 class CreatedAtFilter(filters.BaseFilterBackend):
@@ -62,3 +62,31 @@ def get_schema_operation_parameters(self, view):
                 },
             }
         ]
+
+
+class PaymentSettledFilter(filters.BaseFilterBackend):
+    """Allows you to filter by settled status."""
+
+    def filter_queryset(self, request, queryset, view):
+        settled = request.query_params.get("settled", None)
+
+        if settled is None:
+            return queryset
+
+        if strtobool(settled):
+            return queryset.filter(batch__processed=True)
+
+        return queryset.exclude(batch__processed=True)
+
+    def get_schema_operation_parameters(self, view):
+        return [
+            {
+                "name": "settled",
+                "required": False,
+                "in": "query",
+                "description": "Filter by settled status",
+                "schema": {
+                    "type": "boolean",
+                },
+            }
+        ]
diff --git a/website/payments/api/v2/views.py b/website/payments/api/v2/views.py
--- a/website/payments/api/v2/views.py
+++ b/website/payments/api/v2/views.py
@@ -31,6 +31,7 @@ class PaymentListView(ListAPIView):
         framework_filters.OrderingFilter,
         filters.CreatedAtFilter,
         filters.PaymentTypeFilter,
+        filters.PaymentSettledFilter,
     )
     ordering_fields = ("created_at",)
 


Test patch:
diff --git a/website/payments/tests/api/v2/__init__.py b/website/payments/tests/api/v2/__init__.py
new file mode 100644
diff --git a/website/payments/tests/api/v2/test_views.py b/website/payments/tests/api/v2/test_views.py
new file mode 100644
--- /dev/null
+++ b/website/payments/tests/api/v2/test_views.py
@@ -0,0 +1,91 @@
+from django.test import Client, TestCase, override_settings
+from django.urls import reverse
+from django.utils import timezone
+
+from freezegun import freeze_time
+
+from payments.models import BankAccount, Batch, Payment, PaymentUser
+
+
+@freeze_time("2019-04-01")
+@override_settings(SUSPEND_SIGNALS=True)
+class PaymentListViewTest(TestCase):
+    """Test for the PaymentListView."""
+
+    fixtures = ["members.json"]
+
+    @classmethod
+    def setUpTestData(cls):
+        cls.login_user = PaymentUser.objects.filter(last_name="Wiggers").first()
+        cls.account1 = BankAccount.objects.create(
+            owner=cls.login_user,
+            initials="J1",
+            last_name="Test",
+            iban="NL91ABNA0417164300",
+            valid_from="2019-03-01",
+            signature="sig",
+            mandate_no="11-2",
+        )
+        cls.payment1 = Payment.objects.create(
+            created_at=timezone.datetime(year=2019, month=3, day=1),
+            paid_by=cls.login_user,
+            processed_by=cls.login_user,
+            notes="Testing Payment 1",
+            amount=10,
+            type=Payment.TPAY,
+        )
+
+    def setUp(self):
+        self.account1.refresh_from_db()
+        self.payment1.refresh_from_db()
+        self.client = Client()
+        self.client.force_login(self.login_user)
+
+    def test_settled_filter(self):
+        """Test if the view shows payments."""
+        response = self.client.get(
+            reverse("api:v2:payments:payments-list"),
+            follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertContains(response, "Testing Payment 1")
+
+        response = self.client.get(
+            reverse("api:v2:payments:payments-list") + "?settled=yes",
+            follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertNotContains(response, "Testing Payment 1")
+
+        response = self.client.get(
+            reverse("api:v2:payments:payments-list") + "?settled=no",
+            follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertContains(response, "Testing Payment 1")
+
+        self.payment1.batch = Batch.objects.create(processed=False)
+        self.payment1.save()
+        self.payment1.batch.processed = True
+        self.payment1.batch.save()
+
+        response = self.client.get(
+            reverse("api:v2:payments:payments-list"),
+            follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertContains(response, "Testing Payment 1")
+
+        response = self.client.get(
+            reverse("api:v2:payments:payments-list") + "?settled=yes",
+            follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertContains(response, "Testing Payment 1")
+
+        response = self.client.get(
+            reverse("api:v2:payments:payments-list") + "?settled=no",
+            follow=True,
+        )
+        self.assertEqual(200, response.status_code)
+        self.assertNotContains(response, "Testing Payment 1")