Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions app/my_practice/views/calendar_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
)

Expand All @@ -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),
}
)

Expand All @@ -271,15 +281,14 @@ 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,
"my_practice/calendar_approval_queue.html",
{
"grouped": grouped,
"total_pending": total_pending,
"total_duplicates": total_duplicates,
"stale_count": stale_count,
},
)

Expand Down
27 changes: 19 additions & 8 deletions app/my_practice/views/invoice_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
15 changes: 6 additions & 9 deletions app/templates/my_practice/calendar_approval_queue.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@
</span>
</div>

{% if total_duplicates > 0 %}
<div class="alert-box warning alert-box--flex mb-3">
{% if stale_count > 0 %}
<div class="alert-box info alert-box--flex mb-3">
<span>
<strong>⚠️ {{ total_duplicates }} bereits importiert</strong><br>
<strong>ℹ️ {{ stale_count }} Termin{{ stale_count|pluralize:"e" }} ausgeblendet</strong><br>
<span class="alert-box__hint">
Diese Termine haben bereits passende Rechnungspositionen und können übersprungen werden.
Diese Termine haben bereits passende Rechnungspositionen und werden nicht angezeigt.
</span>
</span>
<button type="button" class="btn btn-danger-solid" id="skip-duplicates-btn">
🚫 Alle Duplikate überspringen
<button type="button" class="btn btn-secondary btn-sm" id="skip-duplicates-btn">
🚫 Als erledigt markieren
</button>
</div>
{% endif %}
Expand Down Expand Up @@ -117,9 +117,6 @@ <h3 class="queue-group-header__title">
{% endif %}
</td>
<td>
{% if event.is_duplicate %}
<span class="badge-duplicate">✓ vorhanden</span>
{% endif %}
<button type="button" class="btn btn-skip skip-event-btn"
data-event-id="{{ event.pk }}"
title="Termin überspringen">
Expand Down