feat(transactions): allow filtering the transaction picker search

TransactionSelect takes a filters dict sent as query params; the search endpoint applies allowlisted ones (currency, account).
This commit is contained in:
Herculino Trotta committed 2026-11-10 21:55:29 -03:00
1 parent eafaf0027c
commit 706f8fea2b
4 files changed
+31 -2

No files matched your search

+6
View File
@@ -1,4 +1,5 @@
import json
from urllib.parse import urlencode
from django.forms import SelectMultiple, widgets
from django.urls import reverse
@@ -141,6 +142,9 @@ class TransactionSelect(TomSelect):
self.load_income = income
self.load_expense = expense
self.create = False
# Extra search filters sent as query params, e.g. {"currency": 1};
# keys must be in SEARCH_FILTERS of the transactions_search view
self.filters = {}
def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super().build_attrs(base_attrs, extra_attrs)
@@ -156,6 +160,8 @@ class TransactionSelect(TomSelect):
attrs["data-load"] = reverse(
"transactions_search", kwargs={"filter_type": "expenses"}
)
if self.filters:
attrs["data-load"] += "?" + urlencode(self.filters)
return attrs
@@ -32,7 +32,7 @@ class TransactionSearchTests(TestCase):
def make(self, days, description="", amount="10", **kwargs):
return Transaction.objects.create(
account=self.account,
account=kwargs.pop("account", self.account),
type=Transaction.Type.EXPENSE,
date=self.today + timedelta(days=days),
reference_date=(self.today + timedelta(days=days)).replace(day=1),
@@ -72,3 +72,13 @@ class TransactionSearchTests(TestCase):
self.assertEqual(self.search("wallet"), ["Groceries", "Other"])
self.assertEqual(self.search("groceries nomatch"), [])
def test_filters_by_currency(self):
usd = Currency.objects.create(code="USD", name="Dollar", decimal_places=2)
other = Account.objects.create(name="Bank", currency=usd)
self.make(0, "brl")
self.make(0, "usd", account=other)
response = self.client.get(
reverse("transactions_search"), {"q": "", "currency": usd.id}
)
self.assertEqual([r["description"] for r in response.json()], ["usd"])
@@ -985,6 +985,13 @@ def transactions_trash_can_list(request):
)
# Query params the transaction picker may filter by (TransactionSelect.filters)
SEARCH_FILTERS = {
"currency": "account__currency_id",
"account": "account_id",
}
@login_required
@require_http_methods(["GET"])
def get_recent_transactions(request, filter_type=None):
@@ -1002,6 +1009,11 @@ def get_recent_transactions(request, filter_type=None):
elif filter_type == "income":
queryset = queryset.filter(type=Transaction.Type.INCOME)
for param, lookup in SEARCH_FILTERS.items():
value = request.GET.get(param, "")
if value.isdigit():
queryset = queryset.filter(**{lookup: value})
if search_term:
queryset = search_transactions(queryset, search_term)
+2 -1
View File
@@ -136,7 +136,8 @@ window.TomSelect = function createDynamicTomSelect(element) {
config.preload = 'focus';
config.shouldLoad = () => true;
config.load = function (query, callback) {
let url = element.dataset.load + '?q=' + encodeURIComponent(query);
const url = new URL(element.dataset.load, window.location.origin);
url.searchParams.set('q', query);
fetch(url)
.then(response => response.json())
.then(json => {