mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-11-13 12:23:44 +01:00
fix(transactions): keep transaction picker results in monthly view order
Results come back as today, tomorrow, yesterday, upcoming (soonest first) and then past (newest first); past dates used to be oldest first. The endpoint is now capped at 100 results, as its docstring already claimed. tom-select also re-scored and re-sorted the server's results on the client, which scrambled the order while searching. Load-based selects now keep the server's order, drop the previous query's results, and preload recent transactions on focus.
This commit is contained in:
1 parent
aa3ffe9b3e
commit
8482c85a76
2 files changed
+34
-27
No files matched your search
@@ -25,7 +25,7 @@ from dateutil.relativedelta import relativedelta
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db.models import Case, IntegerField, Q, Value, When
|
from django.db.models import Case, F, IntegerField, Q, Value, When
|
||||||
from django.http import FileResponse, Http404, HttpResponse, JsonResponse, QueryDict
|
from django.http import FileResponse, Http404, HttpResponse, JsonResponse, QueryDict
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -972,40 +972,20 @@ def transactions_trash_can_list(request):
|
|||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def get_recent_transactions(request, filter_type=None):
|
def get_recent_transactions(request, filter_type=None):
|
||||||
"""Return the 100 most recent non-deleted transactions with optional search."""
|
"""Return up to 100 non-deleted transactions, ordered like the monthly view, with optional search."""
|
||||||
# Get search term from query params
|
|
||||||
search_term = request.GET.get("q", "").strip()
|
search_term = request.GET.get("q", "").strip()
|
||||||
|
|
||||||
today = timezone.localdate(timezone.now())
|
today = timezone.localdate(timezone.now())
|
||||||
yesterday = today - timezone.timedelta(days=1)
|
yesterday = today - timezone.timedelta(days=1)
|
||||||
tomorrow = today + timezone.timedelta(days=1)
|
tomorrow = today + timezone.timedelta(days=1)
|
||||||
|
|
||||||
# Base queryset with selected fields
|
queryset = Transaction.objects.filter(deleted=False)
|
||||||
queryset = (
|
|
||||||
Transaction.objects.filter(deleted=False)
|
|
||||||
.annotate(
|
|
||||||
date_order=Case(
|
|
||||||
When(date=today, then=Value(0)),
|
|
||||||
When(date=tomorrow, then=Value(1)),
|
|
||||||
When(date=yesterday, then=Value(2)),
|
|
||||||
When(date__gt=tomorrow, then=Value(3)),
|
|
||||||
When(date__lt=yesterday, then=Value(4)),
|
|
||||||
default=Value(5),
|
|
||||||
output_field=IntegerField(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.select_related("account__currency", "account__group", "category")
|
|
||||||
.prefetch_related("tags", "entities")
|
|
||||||
.order_by("date_order", "date", "id")
|
|
||||||
)
|
|
||||||
|
|
||||||
if filter_type:
|
if filter_type == "expenses":
|
||||||
if filter_type == "expenses":
|
queryset = queryset.filter(type=Transaction.Type.EXPENSE)
|
||||||
queryset = queryset.filter(type=Transaction.Type.EXPENSE)
|
elif filter_type == "income":
|
||||||
elif filter_type == "income":
|
queryset = queryset.filter(type=Transaction.Type.INCOME)
|
||||||
queryset = queryset.filter(type=Transaction.Type.INCOME)
|
|
||||||
|
|
||||||
# Apply search if provided
|
|
||||||
if search_term:
|
if search_term:
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
Q(description__icontains=search_term)
|
Q(description__icontains=search_term)
|
||||||
@@ -1015,4 +995,24 @@ def get_recent_transactions(request, filter_type=None):
|
|||||||
| Q(category__name__icontains=search_term)
|
| Q(category__name__icontains=search_term)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Today, tomorrow, yesterday, then upcoming (soonest first), then past (newest first)
|
||||||
|
queryset = (
|
||||||
|
queryset.annotate(
|
||||||
|
date_order=Case(
|
||||||
|
When(date=today, then=Value(0)),
|
||||||
|
When(date=tomorrow, then=Value(1)),
|
||||||
|
When(date=yesterday, then=Value(2)),
|
||||||
|
When(date__gt=tomorrow, then=Value(3)),
|
||||||
|
default=Value(4),
|
||||||
|
output_field=IntegerField(),
|
||||||
|
),
|
||||||
|
upcoming_date=Case(When(date__gt=tomorrow, then=F("date"))),
|
||||||
|
)
|
||||||
|
.select_related("account__currency", "account__group", "category")
|
||||||
|
.prefetch_related("tags", "entities")
|
||||||
|
.order_by(
|
||||||
|
"date_order", F("upcoming_date").asc(nulls_last=True), "-date", "-id"
|
||||||
|
)[:100]
|
||||||
|
)
|
||||||
|
|
||||||
return JsonResponse([transaction_option(t) for t in queryset], safe=False)
|
return JsonResponse([transaction_option(t) for t in queryset], safe=False)
|
||||||
@@ -130,11 +130,18 @@ window.TomSelect = function createDynamicTomSelect(element) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (element.dataset.load) {
|
if (element.dataset.load) {
|
||||||
|
// The server searches and orders; don't let tom-select re-filter or re-sort the results
|
||||||
|
config.score = () => () => 1;
|
||||||
|
config.sortField = [{field: '$order'}];
|
||||||
|
config.preload = 'focus';
|
||||||
|
config.shouldLoad = () => true;
|
||||||
config.load = function (query, callback) {
|
config.load = function (query, callback) {
|
||||||
let url = element.dataset.load + '?q=' + encodeURIComponent(query);
|
let url = element.dataset.load + '?q=' + encodeURIComponent(query);
|
||||||
fetch(url)
|
fetch(url)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
|
// Drop results from the previous query, keeping the selected item
|
||||||
|
this.clearOptions();
|
||||||
callback(json);
|
callback(json);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
callback();
|
callback();
|
||||||
|
|||||||
Reference in new issue
Block a user