feat: destaca fatura paga nos cartoes

This commit is contained in:
Felipe Coutinho
2026-06-06 16:31:33 -03:00
parent b443fb010a
commit 356801324c
6 changed files with 130 additions and 75 deletions

View File

@@ -11,7 +11,11 @@ import {
} from "drizzle-orm";
import { cards, financialAccounts, invoices, transactions } from "@/db/schema";
import { db } from "@/shared/lib/db";
import { INVOICE_PAYMENT_STATUS } from "@/shared/lib/invoices";
import {
INVOICE_PAYMENT_STATUS,
INVOICE_STATUS_VALUES,
type InvoicePaymentStatus,
} from "@/shared/lib/invoices";
import { loadLogoOptions } from "@/shared/lib/logo/options";
import {
formatPeriodMonthShort,
@@ -33,6 +37,7 @@ type CardData = {
limitAvailable: number;
currentInvoiceAmount: number;
currentInvoiceLabel: string;
currentInvoiceStatus: InvoicePaymentStatus | null;
accountId: string;
accountName: string;
};
@@ -48,6 +53,12 @@ function formatCurrentInvoiceLabel(period: string) {
return `Fatura ${formatPeriodMonthShort(period)}. ${year}`;
}
function parseInvoiceStatus(value: unknown): InvoicePaymentStatus | null {
return INVOICE_STATUS_VALUES.includes(value as InvoicePaymentStatus)
? (value as InvoicePaymentStatus)
: null;
}
async function fetchCardsByStatus(
userId: string,
archived: boolean,
@@ -58,79 +69,94 @@ async function fetchCardsByStatus(
}> {
const currentPeriod = getCurrentPeriod();
const currentInvoiceLabel = formatCurrentInvoiceLabel(currentPeriod);
const [cardRows, accountRows, logoOptions, usageRows, invoiceRows] =
await Promise.all([
db.query.cards.findMany({
orderBy: (table, { desc }) => [desc(table.name)],
where: and(
eq(cards.userId, userId),
archived
? ilike(cards.status, "inativo")
: not(ilike(cards.status, "inativo")),
),
with: {
financialAccount: {
columns: {
id: true,
name: true,
},
const [
cardRows,
accountRows,
logoOptions,
usageRows,
invoiceRows,
invoiceStatusRows,
] = await Promise.all([
db.query.cards.findMany({
orderBy: (table, { desc }) => [desc(table.name)],
where: and(
eq(cards.userId, userId),
archived
? ilike(cards.status, "inativo")
: not(ilike(cards.status, "inativo")),
),
with: {
financialAccount: {
columns: {
id: true,
name: true,
},
},
}),
db.query.financialAccounts.findMany({
orderBy: (table, { desc }) => [desc(table.name)],
where: eq(financialAccounts.userId, userId),
columns: {
id: true,
name: true,
logo: true,
},
}),
loadLogoOptions(),
db
.select({
cardId: transactions.cardId,
total: sql<number>`coalesce(sum(${transactions.amount}), 0)`,
})
.from(transactions)
.leftJoin(
invoices,
and(
eq(invoices.userId, transactions.userId),
eq(invoices.cardId, transactions.cardId),
eq(invoices.period, transactions.period),
},
}),
db.query.financialAccounts.findMany({
orderBy: (table, { desc }) => [desc(table.name)],
where: eq(financialAccounts.userId, userId),
columns: {
id: true,
name: true,
logo: true,
},
}),
loadLogoOptions(),
db
.select({
cardId: transactions.cardId,
total: sql<number>`coalesce(sum(${transactions.amount}), 0)`,
})
.from(transactions)
.leftJoin(
invoices,
and(
eq(invoices.userId, transactions.userId),
eq(invoices.cardId, transactions.cardId),
eq(invoices.period, transactions.period),
),
)
.where(
and(
eq(transactions.userId, userId),
isNotNull(transactions.cardId),
or(
isNull(invoices.paymentStatus),
ne(invoices.paymentStatus, INVOICE_PAYMENT_STATUS.PAID),
),
)
.where(
and(
eq(transactions.userId, userId),
isNotNull(transactions.cardId),
or(
isNull(invoices.paymentStatus),
ne(invoices.paymentStatus, INVOICE_PAYMENT_STATUS.PAID),
),
// Recorrente no cartão: só consome limite quando a data da ocorrência já passou
or(
ne(transactions.condition, "Recorrente"),
sql`${transactions.purchaseDate} <= current_date`,
),
// Recorrente no cartão: só consome limite quando a data da ocorrência já passou
or(
ne(transactions.condition, "Recorrente"),
sql`${transactions.purchaseDate} <= current_date`,
),
)
.groupBy(transactions.cardId),
db
.select({
cardId: transactions.cardId,
total: sql<number>`coalesce(sum(${transactions.amount}), 0)`,
})
.from(transactions)
.where(
and(
eq(transactions.userId, userId),
eq(transactions.period, currentPeriod),
),
)
.groupBy(transactions.cardId),
]);
),
)
.groupBy(transactions.cardId),
db
.select({
cardId: transactions.cardId,
total: sql<number>`coalesce(sum(${transactions.amount}), 0)`,
})
.from(transactions)
.where(
and(
eq(transactions.userId, userId),
eq(transactions.period, currentPeriod),
),
)
.groupBy(transactions.cardId),
db
.select({
cardId: invoices.cardId,
paymentStatus: invoices.paymentStatus,
})
.from(invoices)
.where(
and(eq(invoices.userId, userId), eq(invoices.period, currentPeriod)),
),
]);
const usageMap = new Map<string, number>();
usageRows.forEach((row: { cardId: string | null; total: number | null }) => {
@@ -144,6 +170,13 @@ async function fetchCardsByStatus(
invoiceMap.set(row.cardId, Math.abs(Number(row.total ?? 0)));
},
);
const invoiceStatusMap = new Map<string, InvoicePaymentStatus>();
invoiceStatusRows.forEach((row) => {
if (!row.cardId) return;
const status = parseInvoiceStatus(row.paymentStatus);
if (!status) return;
invoiceStatusMap.set(row.cardId, status);
});
const cardList = cardRows.map((card) => ({
id: card.id,
@@ -166,6 +199,7 @@ async function fetchCardsByStatus(
})(),
currentInvoiceAmount: invoiceMap.get(card.id) ?? 0,
currentInvoiceLabel,
currentInvoiceStatus: invoiceStatusMap.get(card.id) ?? null,
accountId: card.accountId,
accountName:
(card.financialAccount as { name?: string } | null)?.name ??