import { and, desc, eq, inArray, isNull, or, sql } from "drizzle-orm"; import { lancamentos, pagadores } from "@/db/schema"; import { ACCOUNT_AUTO_INVOICE_NOTE_PREFIX } from "@/lib/accounts/constants"; import { toNumber } from "@/lib/dashboard/common"; import { db } from "@/lib/db"; import { PAGADOR_ROLE_ADMIN } from "@/lib/pagadores/constants"; import { calculatePercentageChange } from "@/lib/utils/math"; import { getPreviousPeriod } from "@/lib/utils/period"; export type DashboardPagador = { id: string; name: string; email: string | null; avatarUrl: string | null; totalExpenses: number; previousExpenses: number; percentageChange: number | null; isAdmin: boolean; }; export type DashboardPagadoresSnapshot = { pagadores: DashboardPagador[]; totalExpenses: number; }; export async function fetchDashboardPagadores( userId: string, period: string, ): Promise { const previousPeriod = getPreviousPeriod(period); const rows = await db .select({ id: pagadores.id, name: pagadores.name, email: pagadores.email, avatarUrl: pagadores.avatarUrl, role: pagadores.role, period: lancamentos.period, totalExpenses: sql`COALESCE(SUM(ABS(${lancamentos.amount})), 0)`, }) .from(lancamentos) .innerJoin(pagadores, eq(lancamentos.pagadorId, pagadores.id)) .where( and( eq(lancamentos.userId, userId), inArray(lancamentos.period, [period, previousPeriod]), eq(lancamentos.transactionType, "Despesa"), or( isNull(lancamentos.note), sql`${lancamentos.note} NOT LIKE ${`${ACCOUNT_AUTO_INVOICE_NOTE_PREFIX}%`}`, ), ), ) .groupBy( pagadores.id, pagadores.name, pagadores.email, pagadores.avatarUrl, pagadores.role, lancamentos.period, ) .orderBy(desc(sql`SUM(ABS(${lancamentos.amount}))`)); const groupedPagadores = new Map< string, { id: string; name: string; email: string | null; avatarUrl: string | null; isAdmin: boolean; currentExpenses: number; previousExpenses: number; } >(); for (const row of rows) { const entry = groupedPagadores.get(row.id) ?? { id: row.id, name: row.name, email: row.email, avatarUrl: row.avatarUrl, isAdmin: row.role === PAGADOR_ROLE_ADMIN, currentExpenses: 0, previousExpenses: 0, }; const amount = toNumber(row.totalExpenses); if (row.period === period) { entry.currentExpenses = amount; } else { entry.previousExpenses = amount; } groupedPagadores.set(row.id, entry); } const pagadoresList = Array.from(groupedPagadores.values()) .filter((p) => p.currentExpenses > 0) .map((pagador) => ({ id: pagador.id, name: pagador.name, email: pagador.email, avatarUrl: pagador.avatarUrl, totalExpenses: pagador.currentExpenses, previousExpenses: pagador.previousExpenses, percentageChange: calculatePercentageChange( pagador.currentExpenses, pagador.previousExpenses, ), isAdmin: pagador.isAdmin, })) .sort((a, b) => b.totalExpenses - a.totalExpenses); const totalExpenses = pagadoresList.reduce( (sum, p) => sum + p.totalExpenses, 0, ); return { pagadores: pagadoresList, totalExpenses, }; }