mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-11 03:31:47 +00:00
fix: exclui transações de contas fora do saldo nos totais por pessoa e orçamentos
Adicionado leftJoin(financialAccounts) + excludeTransactionsFromExcludedAccounts() em 6 queries de payers/details.ts (totais do mês, histórico, uso de cartões, etc.) e em fetchBudgetsForUser/fetchCategoryBudgetSummary de budgets/queries.ts. Contas marcadas como excludeFromBalance (ex: Ajuste de saldo) não entram mais nos cálculos de gasto, alinhando a tela de Pessoas, Orçamentos e o badge do modal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,12 @@
|
|||||||
import { and, asc, eq, inArray, isNull, or, sql, sum } from "drizzle-orm";
|
import { and, asc, eq, inArray, isNull, or, sql, sum } from "drizzle-orm";
|
||||||
import { budgets, categories, transactions } from "@/db/schema";
|
import {
|
||||||
|
budgets,
|
||||||
|
categories,
|
||||||
|
financialAccounts,
|
||||||
|
transactions,
|
||||||
|
} from "@/db/schema";
|
||||||
import { ACCOUNT_AUTO_INVOICE_NOTE_PREFIX } from "@/shared/lib/accounts/constants";
|
import { ACCOUNT_AUTO_INVOICE_NOTE_PREFIX } from "@/shared/lib/accounts/constants";
|
||||||
|
import { excludeTransactionsFromExcludedAccounts } from "@/shared/lib/accounts/query-filters";
|
||||||
import { db } from "@/shared/lib/db";
|
import { db } from "@/shared/lib/db";
|
||||||
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";
|
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";
|
||||||
|
|
||||||
@@ -75,6 +81,10 @@ export async function fetchBudgetsForUser(
|
|||||||
totalAmount: sum(transactions.amount).as("totalAmount"),
|
totalAmount: sum(transactions.amount).as("totalAmount"),
|
||||||
})
|
})
|
||||||
.from(transactions)
|
.from(transactions)
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(transactions.userId, userId),
|
eq(transactions.userId, userId),
|
||||||
@@ -86,6 +96,7 @@ export async function fetchBudgetsForUser(
|
|||||||
isNull(transactions.note),
|
isNull(transactions.note),
|
||||||
sql`${transactions.note} NOT LIKE ${`${ACCOUNT_AUTO_INVOICE_NOTE_PREFIX}%`}`,
|
sql`${transactions.note} NOT LIKE ${`${ACCOUNT_AUTO_INVOICE_NOTE_PREFIX}%`}`,
|
||||||
),
|
),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.groupBy(transactions.categoryId);
|
.groupBy(transactions.categoryId);
|
||||||
@@ -127,3 +138,57 @@ export async function fetchBudgetsForUser(
|
|||||||
|
|
||||||
return { budgets: budgetList, categoriesOptions };
|
return { budgets: budgetList, categoriesOptions };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CategoryBudgetSummary = {
|
||||||
|
amount: number;
|
||||||
|
spent: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function fetchCategoryBudgetSummary(
|
||||||
|
userId: string,
|
||||||
|
categoryId: string,
|
||||||
|
period: string,
|
||||||
|
): Promise<CategoryBudgetSummary | null> {
|
||||||
|
const [adminPayerId, budget] = await Promise.all([
|
||||||
|
getAdminPayerId(userId),
|
||||||
|
db.query.budgets.findFirst({
|
||||||
|
columns: { amount: true },
|
||||||
|
where: and(
|
||||||
|
eq(budgets.userId, userId),
|
||||||
|
eq(budgets.categoryId, categoryId),
|
||||||
|
eq(budgets.period, period),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!adminPayerId || !budget) return null;
|
||||||
|
|
||||||
|
const totals = await db
|
||||||
|
.select({
|
||||||
|
totalAmount: sum(transactions.amount).as("totalAmount"),
|
||||||
|
})
|
||||||
|
.from(transactions)
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(transactions.userId, userId),
|
||||||
|
eq(transactions.period, period),
|
||||||
|
eq(transactions.transactionType, "Despesa"),
|
||||||
|
eq(transactions.payerId, adminPayerId),
|
||||||
|
eq(transactions.categoryId, categoryId),
|
||||||
|
or(
|
||||||
|
isNull(transactions.note),
|
||||||
|
sql`${transactions.note} NOT LIKE ${`${ACCOUNT_AUTO_INVOICE_NOTE_PREFIX}%`}`,
|
||||||
|
),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
amount: toNumber(budget.amount),
|
||||||
|
spent: Math.abs(toNumber(totals[0]?.totalAmount ?? 0)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import {
|
|||||||
sql,
|
sql,
|
||||||
sum,
|
sum,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
import { cards, transactions } from "@/db/schema";
|
import { cards, financialAccounts, transactions } from "@/db/schema";
|
||||||
import { ACCOUNT_AUTO_INVOICE_NOTE_PREFIX } from "@/shared/lib/accounts/constants";
|
import { ACCOUNT_AUTO_INVOICE_NOTE_PREFIX } from "@/shared/lib/accounts/constants";
|
||||||
|
import { excludeTransactionsFromExcludedAccounts } from "@/shared/lib/accounts/query-filters";
|
||||||
import { db } from "@/shared/lib/db";
|
import { db } from "@/shared/lib/db";
|
||||||
import { toDateOnlyString } from "@/shared/utils/date";
|
import { toDateOnlyString } from "@/shared/utils/date";
|
||||||
import { safeToNumber as toNumber } from "@/shared/utils/number";
|
import { safeToNumber as toNumber } from "@/shared/utils/number";
|
||||||
@@ -96,12 +97,17 @@ export async function fetchPayerMonthlyBreakdown({
|
|||||||
totalAmount: sum(transactions.amount).as("total"),
|
totalAmount: sum(transactions.amount).as("total"),
|
||||||
})
|
})
|
||||||
.from(transactions)
|
.from(transactions)
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(transactions.userId, userId),
|
eq(transactions.userId, userId),
|
||||||
eq(transactions.payerId, payerId),
|
eq(transactions.payerId, payerId),
|
||||||
eq(transactions.period, period),
|
eq(transactions.period, period),
|
||||||
excludeAutoInvoiceEntries(),
|
excludeAutoInvoiceEntries(),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.groupBy(transactions.paymentMethod, transactions.transactionType);
|
.groupBy(transactions.paymentMethod, transactions.transactionType);
|
||||||
@@ -155,6 +161,10 @@ export async function fetchPayerHistory({
|
|||||||
totalAmount: sum(transactions.amount).as("total"),
|
totalAmount: sum(transactions.amount).as("total"),
|
||||||
})
|
})
|
||||||
.from(transactions)
|
.from(transactions)
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(transactions.userId, userId),
|
eq(transactions.userId, userId),
|
||||||
@@ -162,6 +172,7 @@ export async function fetchPayerHistory({
|
|||||||
gte(transactions.period, start),
|
gte(transactions.period, start),
|
||||||
lte(transactions.period, end),
|
lte(transactions.period, end),
|
||||||
excludeAutoInvoiceEntries(),
|
excludeAutoInvoiceEntries(),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.groupBy(transactions.period, transactions.transactionType);
|
.groupBy(transactions.period, transactions.transactionType);
|
||||||
@@ -210,6 +221,10 @@ export async function fetchPayerCardUsage({
|
|||||||
})
|
})
|
||||||
.from(transactions)
|
.from(transactions)
|
||||||
.innerJoin(cards, eq(transactions.cardId, cards.id))
|
.innerJoin(cards, eq(transactions.cardId, cards.id))
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(transactions.userId, userId),
|
eq(transactions.userId, userId),
|
||||||
@@ -217,6 +232,7 @@ export async function fetchPayerCardUsage({
|
|||||||
eq(transactions.period, period),
|
eq(transactions.period, period),
|
||||||
eq(transactions.paymentMethod, PAYMENT_METHOD_CARD),
|
eq(transactions.paymentMethod, PAYMENT_METHOD_CARD),
|
||||||
excludeAutoInvoiceEntries(),
|
excludeAutoInvoiceEntries(),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.groupBy(transactions.cardId, cards.name, cards.logo);
|
.groupBy(transactions.cardId, cards.name, cards.logo);
|
||||||
@@ -251,6 +267,10 @@ export async function fetchPayerBoletoStats({
|
|||||||
totalCount: sql<number>`count(${transactions.id})`.as("count"),
|
totalCount: sql<number>`count(${transactions.id})`.as("count"),
|
||||||
})
|
})
|
||||||
.from(transactions)
|
.from(transactions)
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(transactions.userId, userId),
|
eq(transactions.userId, userId),
|
||||||
@@ -258,6 +278,7 @@ export async function fetchPayerBoletoStats({
|
|||||||
eq(transactions.period, period),
|
eq(transactions.period, period),
|
||||||
eq(transactions.paymentMethod, PAYMENT_METHOD_BOLETO),
|
eq(transactions.paymentMethod, PAYMENT_METHOD_BOLETO),
|
||||||
excludeAutoInvoiceEntries(),
|
excludeAutoInvoiceEntries(),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.groupBy(transactions.isSettled);
|
.groupBy(transactions.isSettled);
|
||||||
@@ -303,6 +324,10 @@ export async function fetchPayerBoletoItems({
|
|||||||
isSettled: transactions.isSettled,
|
isSettled: transactions.isSettled,
|
||||||
})
|
})
|
||||||
.from(transactions)
|
.from(transactions)
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(transactions.userId, userId),
|
eq(transactions.userId, userId),
|
||||||
@@ -310,6 +335,7 @@ export async function fetchPayerBoletoItems({
|
|||||||
eq(transactions.period, period),
|
eq(transactions.period, period),
|
||||||
eq(transactions.paymentMethod, PAYMENT_METHOD_BOLETO),
|
eq(transactions.paymentMethod, PAYMENT_METHOD_BOLETO),
|
||||||
excludeAutoInvoiceEntries(),
|
excludeAutoInvoiceEntries(),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.orderBy(asc(transactions.dueDate));
|
.orderBy(asc(transactions.dueDate));
|
||||||
@@ -343,6 +369,10 @@ export async function fetchPayerPaymentStatus({
|
|||||||
pendingCount: sql<number>`sum(case when (${transactions.isSettled} = false or ${transactions.isSettled} is null) then 1 else 0 end)`,
|
pendingCount: sql<number>`sum(case when (${transactions.isSettled} = false or ${transactions.isSettled} is null) then 1 else 0 end)`,
|
||||||
})
|
})
|
||||||
.from(transactions)
|
.from(transactions)
|
||||||
|
.leftJoin(
|
||||||
|
financialAccounts,
|
||||||
|
eq(transactions.accountId, financialAccounts.id),
|
||||||
|
)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(transactions.userId, userId),
|
eq(transactions.userId, userId),
|
||||||
@@ -350,6 +380,7 @@ export async function fetchPayerPaymentStatus({
|
|||||||
eq(transactions.period, period),
|
eq(transactions.period, period),
|
||||||
eq(transactions.transactionType, DESPESA),
|
eq(transactions.transactionType, DESPESA),
|
||||||
excludeAutoInvoiceEntries(),
|
excludeAutoInvoiceEntries(),
|
||||||
|
excludeTransactionsFromExcludedAccounts(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user