Refine statistics comparisons

This commit is contained in:
2026-08-05 14:44:56 +02:00
parent eafd718e64
commit 4b35dfe55e
3 changed files with 177 additions and 46 deletions
+35 -7
View File
@@ -721,6 +721,8 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
SUM(sb.rounded_minutes) FILTER (WHERE sb.billing_status = 'billed')::int AS billed_minutes,
SUM(sb.rounded_minutes) FILTER (WHERE sb.billing_status = 'non_billable')::int AS non_billable_minutes,
SUM(sb.rounded_minutes) FILTER (WHERE sb.billing_status IS NULL)::int AS open_minutes,
COUNT(*) FILTER (WHERE sb.billing_status = 'billed')::int AS billed_sessions,
COUNT(*) FILTER (WHERE sb.billing_status = 'non_billable')::int AS non_billable_sessions,
COUNT(*) FILTER (WHERE sb.billing_status IS NULL)::int AS open_count,
COUNT(*) FILTER (WHERE sb.recurring_billing_id IS NOT NULL)::int AS recurring_session_count,
COUNT(*) FILTER (WHERE sb.recurring_billing_id IS NULL)::int AS manual_session_count
@@ -759,9 +761,13 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
COALESCE(SUM(rounded_minutes) FILTER (WHERE billing_status = 'billed'), 0)::int AS billed_minutes,
COALESCE(SUM(rounded_minutes) FILTER (WHERE billing_status = 'non_billable'), 0)::int AS non_billable_minutes,
COALESCE(SUM(rounded_minutes) FILTER (WHERE billing_status IS NULL), 0)::int AS open_minutes,
COUNT(*) FILTER (WHERE billing_status = 'billed')::int AS billed_sessions,
COUNT(*) FILTER (WHERE billing_status = 'non_billable')::int AS non_billable_sessions,
COUNT(*) FILTER (WHERE billing_status IS NULL)::int AS open_sessions,
COALESCE(SUM(rounded_minutes) FILTER (WHERE recurring_billing_id IS NOT NULL), 0)::int AS recurring_minutes,
COALESCE(SUM(rounded_minutes) FILTER (WHERE recurring_billing_id IS NULL), 0)::int AS manual_minutes,
COUNT(*) FILTER (WHERE recurring_billing_id IS NOT NULL)::int AS recurring_sessions,
COUNT(*) FILTER (WHERE recurring_billing_id IS NULL)::int AS manual_sessions,
COALESCE(ROUND(AVG(rounded_minutes)), 0)::int AS average_session_minutes,
COUNT(DISTINCT day)::int AS active_days,
COALESCE((SELECT SUM(crm_billed_minutes)::int FROM ticket_day_billing), 0)::int AS crm_billed_minutes
@@ -779,6 +785,9 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
COALESCE(day_sessions.billed_minutes, 0)::int AS billed_minutes,
COALESCE(day_sessions.non_billable_minutes, 0)::int AS non_billable_minutes,
COALESCE(day_sessions.open_minutes, 0)::int AS open_minutes,
COALESCE(day_sessions.billed_sessions, 0)::int AS billed_sessions,
COALESCE(day_sessions.non_billable_sessions, 0)::int AS non_billable_sessions,
COALESCE(day_sessions.open_sessions, 0)::int AS open_sessions,
COALESCE(SUM(tdb.crm_billed_minutes), 0)::int AS crm_billed_minutes
FROM (
SELECT day, SUM(tracked_minutes)::int AS tracked_minutes
@@ -791,12 +800,15 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
SUM(session_count)::int AS session_count,
SUM(COALESCE(billed_minutes, 0))::int AS billed_minutes,
SUM(COALESCE(non_billable_minutes, 0))::int AS non_billable_minutes,
SUM(COALESCE(open_minutes, 0))::int AS open_minutes
SUM(COALESCE(open_minutes, 0))::int AS open_minutes,
SUM(billed_sessions)::int AS billed_sessions,
SUM(non_billable_sessions)::int AS non_billable_sessions,
SUM(open_count)::int AS open_sessions
FROM session_group
GROUP BY day
) day_sessions ON day_sessions.day = td.day
LEFT JOIN ticket_day_billing tdb ON tdb.day = td.day
GROUP BY td.day, td.tracked_minutes, day_sessions.session_count, day_sessions.billed_minutes, day_sessions.non_billable_minutes, day_sessions.open_minutes
GROUP BY td.day, td.tracked_minutes, day_sessions.session_count, day_sessions.billed_minutes, day_sessions.non_billable_minutes, day_sessions.open_minutes, day_sessions.billed_sessions, day_sessions.non_billable_sessions, day_sessions.open_sessions
ORDER BY td.day ASC;
`,
[period.startIso, period.endIso, userId]
@@ -813,6 +825,9 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
COALESCE(SUM(COALESCE(billed_minutes, 0)), 0)::int AS billed_minutes,
COALESCE(SUM(COALESCE(non_billable_minutes, 0)), 0)::int AS non_billable_minutes,
COALESCE(SUM(COALESCE(open_minutes, 0)), 0)::int AS open_minutes,
COALESCE(SUM(billed_sessions), 0)::int AS billed_sessions,
COALESCE(SUM(non_billable_sessions), 0)::int AS non_billable_sessions,
COALESCE(SUM(open_count), 0)::int AS open_sessions,
COALESCE(SUM(crm_billed_minutes), 0)::int AS crm_billed_minutes
FROM session_group_with_crm
GROUP BY organization_id, organization_name
@@ -831,6 +846,9 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
COALESCE(SUM(COALESCE(billed_minutes, 0)), 0)::int AS billed_minutes,
COALESCE(SUM(COALESCE(non_billable_minutes, 0)), 0)::int AS non_billable_minutes,
COALESCE(SUM(COALESCE(open_minutes, 0)), 0)::int AS open_minutes,
COALESCE(SUM(billed_sessions), 0)::int AS billed_sessions,
COALESCE(SUM(non_billable_sessions), 0)::int AS non_billable_sessions,
COALESCE(SUM(open_count), 0)::int AS open_sessions,
COALESCE(SUM(crm_billed_minutes), 0)::int AS crm_billed_minutes
FROM session_group_with_crm
GROUP BY work_type
@@ -852,6 +870,8 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
COALESCE(SUM(rounded_minutes) FILTER (WHERE billing_status = 'billed'), 0)::int AS billed_minutes,
COALESCE(SUM(rounded_minutes) FILTER (WHERE billing_status = 'non_billable'), 0)::int AS non_billable_minutes,
COALESCE(SUM(rounded_minutes) FILTER (WHERE billing_status IS NULL), 0)::int AS open_minutes,
COUNT(*) FILTER (WHERE billing_status = 'billed')::int AS billed_sessions,
COUNT(*) FILTER (WHERE billing_status = 'non_billable')::int AS non_billable_sessions,
COUNT(*) FILTER (WHERE billing_status IS NULL)::int AS open_sessions,
COALESCE((SELECT SUM(crm_billed_minutes)::int FROM ticket_day_billing tdb WHERE tdb.ticket_id = session_base.ticket_id), 0)::int AS crm_billed_minutes
FROM session_base
@@ -913,7 +933,7 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
tdb.day::text AS day,
tdb.tracked_minutes,
tdb.crm_billed_minutes,
(tdb.tracked_minutes - tdb.crm_billed_minutes)::int AS delta_minutes
(tdb.crm_billed_minutes - tdb.tracked_minutes)::int AS delta_minutes
FROM ticket_day_billing tdb
JOIN session_base sb ON sb.ticket_id = tdb.ticket_id AND sb.day = tdb.day
WHERE tdb.has_crm_value
@@ -934,9 +954,13 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
billed_minutes: 0,
non_billable_minutes: 0,
open_minutes: 0,
billed_sessions: 0,
non_billable_sessions: 0,
open_sessions: 0,
recurring_minutes: 0,
manual_minutes: 0,
recurring_sessions: 0,
manual_sessions: 0,
average_session_minutes: 0,
active_days: 0,
crm_billed_minutes: 0
@@ -954,26 +978,30 @@ async function getStatisticsOverview(period: ParsedPeriod, userId: string) {
billedMinutes: totals.billed_minutes,
nonBillableMinutes: totals.non_billable_minutes,
openMinutes: totals.open_minutes,
billedSessions: totals.billed_sessions,
nonBillableSessions: totals.non_billable_sessions,
openSessions: totals.open_sessions,
recurringMinutes: totals.recurring_minutes,
manualMinutes: totals.manual_minutes,
recurringSessions: totals.recurring_sessions,
manualSessions: totals.manual_sessions,
averageSessionMinutes: totals.average_session_minutes,
activeDays: totals.active_days,
crmBilledMinutes: totals.crm_billed_minutes,
crmDeltaMinutes: totals.minutes - totals.crm_billed_minutes
crmDeltaMinutes: totals.crm_billed_minutes - totals.minutes
},
dailySeries: dailyResult.rows,
organizations: organizationResult.rows.map((row: any) => ({
...row,
crm_delta_minutes: row.total_minutes - row.crm_billed_minutes
crm_delta_minutes: row.crm_billed_minutes - row.total_minutes
})),
workTypes: workTypeResult.rows.map((row: any) => ({
...row,
crm_delta_minutes: row.total_minutes - row.crm_billed_minutes
crm_delta_minutes: row.crm_billed_minutes - row.total_minutes
})),
tickets: ticketResult.rows.map((row: any) => ({
...row,
crm_delta_minutes: row.total_minutes - row.crm_billed_minutes
crm_delta_minutes: row.crm_billed_minutes - row.total_minutes
})),
attention: {
openSessions: openResult.rows,