import { db } from "@/db"; import { clients, payments, time_entries } from "@/db/schema"; import { sql, and, eq } from "drizzle-orm"; export async function getAnalyticsByYear(year: number) { const [contracted] = await db .select({ total: sql`coalesce(sum(${clients.accepted_total}::numeric), 0)` }) .from(clients) .where(sql`extract(year from ${clients.created_at}) = ${year}`); const [clientsRow] = await db .select({ count: sql`count(*)` }) .from(clients) .where(sql`extract(year from ${clients.created_at}) = ${year}`); const [collected] = await db .select({ total: sql`coalesce(sum(${payments.amount}::numeric), 0)` }) .from(payments) .where( and( eq(payments.status, "saldato"), sql`${payments.paid_at} is not null and extract(year from ${payments.paid_at}) = ${year}` ) ); const [pending] = await db .select({ total: sql`coalesce(sum(${payments.amount}::numeric), 0)` }) .from(payments) .where(sql`${payments.status} in ('da_saldare', 'inviata')`); return { contracted: parseFloat(contracted?.total ?? "0"), collected: parseFloat(collected?.total ?? "0"), clientsAcquired: parseInt(clientsRow?.count ?? "0"), pending: parseFloat(pending?.total ?? "0"), }; } export async function getMonthlyCollected(year: number): Promise { const rows = await db .select({ month: sql`extract(month from ${payments.paid_at})::int`, total: sql`coalesce(sum(${payments.amount}::numeric), 0)`, }) .from(payments) .where( and( eq(payments.status, "saldato"), sql`${payments.paid_at} is not null and extract(year from ${payments.paid_at}) = ${year}` ) ) .groupBy(sql`extract(month from ${payments.paid_at})`); const byMonth: number[] = Array(12).fill(0); for (const row of rows) { byMonth[(row.month as number) - 1] = parseFloat(row.total); } return byMonth; } export async function getAvailableYears(): Promise { const rows = await db .select({ year: sql`extract(year from ${clients.created_at})::int` }) .from(clients) .groupBy(sql`extract(year from ${clients.created_at})`); const years = rows.map((r) => r.year as number); const currentYear = new Date().getFullYear(); if (!years.includes(currentYear)) years.push(currentYear); return years.sort((a, b) => b - a); } // ── Time tracking ───────────────────────────────────────────────────────────── export type ClientTimeRow = { clientId: string; clientName: string; totalSeconds: number; }; export async function getTimeByClient(year: number): Promise { const rows = await db .select({ client_id: time_entries.client_id, total: sql`coalesce(sum(${time_entries.duration_seconds}), 0)`, }) .from(time_entries) .where( sql`${time_entries.ended_at} is not null and extract(year from ${time_entries.started_at}) = ${year}` ) .groupBy(time_entries.client_id); if (rows.length === 0) return []; const allClients = await db.select({ id: clients.id, name: clients.name }).from(clients); const nameMap = new Map(allClients.map((c) => [c.id, c.name])); return rows .map((r) => ({ clientId: r.client_id, clientName: nameMap.get(r.client_id) ?? r.client_id, totalSeconds: parseInt(r.total), })) .sort((a, b) => b.totalSeconds - a.totalSeconds); } export async function getTotalTrackedHours(year: number): Promise { const [row] = await db .select({ total: sql`coalesce(sum(${time_entries.duration_seconds}), 0)` }) .from(time_entries) .where( sql`${time_entries.ended_at} is not null and extract(year from ${time_entries.started_at}) = ${year}` ); return Math.round(parseInt(row?.total ?? "0") / 3600 * 10) / 10; }