fix(financeiro): alinhar saldo, métricas e relatórios

This commit is contained in:
Felipe Coutinho
2026-04-03 18:10:43 +00:00
parent acaf9d5c27
commit 549a5bdba1
32 changed files with 960 additions and 118 deletions

View File

@@ -17,12 +17,18 @@ import type { PaymentMethodsData } from "@/features/dashboard/payments/payment-m
import type { PaymentStatusData } from "@/features/dashboard/payments/payment-status-queries";
import type { PurchasesByCategoryData } from "@/features/dashboard/purchases-by-category-queries";
import type { TopEstablishmentsData } from "@/features/dashboard/top-establishments-queries";
import { excludeTransactionsFromExcludedAccounts } from "@/features/dashboard/transaction-filters";
import {
ACCOUNT_AUTO_INVOICE_NOTE_PREFIX,
INITIAL_BALANCE_NOTE,
} from "@/shared/lib/accounts/constants";
import { db } from "@/shared/lib/db";
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";
import {
compareDateOnly,
getBusinessDateString,
isDateOnlyPast,
} from "@/shared/utils/date";
import { safeToNumber as toNumber } from "@/shared/utils/number";
const PAYMENT_METHOD_BOLETO = "Boleto";
@@ -54,6 +60,7 @@ type CurrentPeriodTransactionRow = {
categoryType: string | null;
cardLogo: string | null;
accountLogo: string | null;
accountExcludeInitialBalanceFromIncome: boolean | null;
};
type CategoryOption = PurchasesByCategoryData["categories"][number];
@@ -112,6 +119,21 @@ const shouldIncludeWithoutAutoInvoice = (note: string | null | undefined) =>
const shouldIncludeWithoutAutoGenerated = (note: string | null | undefined) =>
!isInitialBalanceNote(note) && !isAutoInvoiceNote(note);
const shouldIncludeInPaymentStatus = (row: CurrentPeriodTransactionRow) => {
if (!shouldIncludeWithoutAutoInvoice(row.note)) {
return false;
}
if (
isInitialBalanceNote(row.note) &&
row.accountExcludeInitialBalanceFromIncome === true
) {
return false;
}
return true;
};
const shouldIncludeNamedItem = (name: string) => {
const normalized = name.trim().toLowerCase();
@@ -126,9 +148,30 @@ const shouldIncludeNamedItem = (name: string) => {
return true;
};
const compareDateOnlyAscWithNullsLast = (
left: string | null,
right: string | null,
) => {
if (!left && !right) return 0;
if (!left) return 1;
if (!right) return -1;
return compareDateOnly(left, right);
};
const compareDateOnlyDescWithNullsLast = (
left: string | null,
right: string | null,
) => {
if (!left && !right) return 0;
if (!left) return 1;
if (!right) return -1;
return compareDateOnly(right, left);
};
const buildBillsSnapshot = (
rows: CurrentPeriodTransactionRow[],
): DashboardBillsSnapshot => {
const today = getBusinessDateString();
const bills = rows
.filter((row) => row.paymentMethod === PAYMENT_METHOD_BOLETO)
.map((row) => ({
@@ -143,17 +186,44 @@ const buildBillsSnapshot = (
}))
.sort((a, b) => {
if (a.isSettled !== b.isSettled) {
return Number(a.isSettled) - Number(b.isSettled);
return a.isSettled ? 1 : -1;
}
const dueA = a.dueDate
? new Date(a.dueDate).getTime()
: Number.POSITIVE_INFINITY;
const dueB = b.dueDate
? new Date(b.dueDate).getTime()
: Number.POSITIVE_INFINITY;
if (dueA !== dueB) {
return dueA - dueB;
if (!a.isSettled && !b.isSettled) {
const aIsOverdue = a.dueDate ? isDateOnlyPast(a.dueDate, today) : false;
const bIsOverdue = b.dueDate ? isDateOnlyPast(b.dueDate, today) : false;
if (aIsOverdue !== bIsOverdue) {
return aIsOverdue ? -1 : 1;
}
const dueDateDiff = compareDateOnlyAscWithNullsLast(
a.dueDate,
b.dueDate,
);
if (dueDateDiff !== 0) {
return dueDateDiff;
}
const amountDiff = b.amount - a.amount;
if (amountDiff !== 0) {
return amountDiff;
}
}
if (a.isSettled && b.isSettled) {
const paidAtDiff = compareDateOnlyDescWithNullsLast(
a.boletoPaymentDate,
b.boletoPaymentDate,
);
if (paidAtDiff !== 0) {
return paidAtDiff;
}
const amountDiff = b.amount - a.amount;
if (amountDiff !== 0) {
return amountDiff;
}
}
return a.name.localeCompare(b.name, "pt-BR");
@@ -181,7 +251,7 @@ const buildPaymentStatusData = (
for (const row of rows) {
if (
!shouldIncludeWithoutAutoInvoice(row.note) ||
!shouldIncludeInPaymentStatus(row) ||
(row.transactionType !== TRANSACTION_TYPE_INCOME &&
row.transactionType !== TRANSACTION_TYPE_EXPENSE)
) {
@@ -496,6 +566,8 @@ export async function fetchDashboardCurrentPeriodOverview(
categoryType: categories.type,
cardLogo: cards.logo,
accountLogo: financialAccounts.logo,
accountExcludeInitialBalanceFromIncome:
financialAccounts.excludeInitialBalanceFromIncome,
})
.from(transactions)
.leftJoin(cards, eq(transactions.cardId, cards.id))
@@ -509,6 +581,7 @@ export async function fetchDashboardCurrentPeriodOverview(
eq(transactions.userId, userId),
eq(transactions.period, period),
eq(transactions.payerId, adminPayerId),
excludeTransactionsFromExcludedAccounts(),
),
)
.orderBy(