diff --git a/app/my_practice/views/calendar_views.py b/app/my_practice/views/calendar_views.py index 3c59366..61fc995 100644 --- a/app/my_practice/views/calendar_views.py +++ b/app/my_practice/views/calendar_views.py @@ -228,13 +228,24 @@ def calendar_approval_queue(request: HttpRequest) -> HttpResponse: session__duration__lte=OuterRef("duration_minutes") + 5, ) + # Stale count: already-billed events still sitting as pending (shown separately, not in list) + stale_count = ( + PendingCalendarEvent.objects.filter( + practice=practice, + status=PendingCalendarEvent.Status.PENDING, + ) + .annotate(is_duplicate=Exists(duplicate_subquery)) + .filter(is_duplicate=True) + .count() + ) + pending_events = ( PendingCalendarEvent.objects.filter( practice=practice, status=PendingCalendarEvent.Status.PENDING, ) .select_related("matched_client", "suggested_service_type") - .annotate(is_duplicate=Exists(duplicate_subquery)) + .exclude(Exists(duplicate_subquery)) .order_by("matched_client__client_code", "event_date") ) @@ -251,7 +262,6 @@ def calendar_approval_queue(request: HttpRequest) -> HttpResponse: "month": month, "events": events, "count": len(events), - "duplicate_count": sum(1 for e in events if e.is_duplicate), } ) @@ -271,7 +281,6 @@ def calendar_approval_queue(request: HttpRequest) -> HttpResponse: group["invoices"] = draft_invoice_map.get(group["client"].pk, []) if group["client"] else [] total_pending = pending_events.count() - total_duplicates = sum(1 for e in pending_events if e.is_duplicate) return render( request, @@ -279,7 +288,7 @@ def calendar_approval_queue(request: HttpRequest) -> HttpResponse: { "grouped": grouped, "total_pending": total_pending, - "total_duplicates": total_duplicates, + "stale_count": stale_count, }, ) diff --git a/app/my_practice/views/invoice_views.py b/app/my_practice/views/invoice_views.py index 2a11b6f..926596c 100644 --- a/app/my_practice/views/invoice_views.py +++ b/app/my_practice/views/invoice_views.py @@ -11,7 +11,7 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.db import transaction -from django.db.models import Case, Count, DecimalField, F, Q, QuerySet, Sum, When +from django.db.models import Case, Count, DecimalField, Exists, F, OuterRef, Q, QuerySet, Sum, When from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse, reverse_lazy from django.utils.html import format_html @@ -720,14 +720,25 @@ def _gather_billing_data(practice, year, month_num): ): unbilled_sessions_by_client.setdefault(session.client_id, []).append(session) + already_billed_subquery = InvoiceItem.objects.filter( + invoice__client=OuterRef("matched_client"), + session__session_date=OuterRef("event_date"), + session__duration__gte=OuterRef("duration_minutes") - 5, + session__duration__lte=OuterRef("duration_minutes") + 5, + ).exclude(invoice__status=Invoice.Status.CANCELLED) + pending_by_client: dict[int, int] = {} - for row in PendingCalendarEvent.objects.filter( - practice=practice, - event_date__year=year, - event_date__month=month_num, - status=PendingCalendarEvent.Status.PENDING, - matched_client__isnull=False, - ).values("matched_client_id"): + for row in ( + PendingCalendarEvent.objects.filter( + practice=practice, + event_date__year=year, + event_date__month=month_num, + status=PendingCalendarEvent.Status.PENDING, + matched_client__isnull=False, + ) + .exclude(Exists(already_billed_subquery)) + .values("matched_client_id") + ): cid = row["matched_client_id"] pending_by_client[cid] = pending_by_client.get(cid, 0) + 1 diff --git a/app/templates/my_practice/calendar_approval_queue.html b/app/templates/my_practice/calendar_approval_queue.html index db104ad..ca43b75 100644 --- a/app/templates/my_practice/calendar_approval_queue.html +++ b/app/templates/my_practice/calendar_approval_queue.html @@ -27,16 +27,16 @@ -{% if total_duplicates > 0 %} -
+{% if stale_count > 0 %} +
- ⚠️ {{ total_duplicates }} bereits importiert
+ ℹ️ {{ stale_count }} Termin{{ stale_count|pluralize:"e" }} ausgeblendet
- Diese Termine haben bereits passende Rechnungspositionen und können übersprungen werden. + Diese Termine haben bereits passende Rechnungspositionen und werden nicht angezeigt.
-
{% endif %} @@ -117,9 +117,6 @@

{% endif %} - {% if event.is_duplicate %} - ✓ vorhanden - {% endif %}