mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-07-09 03:16:01 +00:00
feat(lancamentos): oculta parcelas antecipadas por preferencia
This commit is contained in:
1
drizzle/0032_bumpy_spencer_smythe.sql
Normal file
1
drizzle/0032_bumpy_spencer_smythe.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "preferencias_usuario" ADD COLUMN "ocultar_parcelas_antecipadas" boolean DEFAULT false NOT NULL;
|
||||||
2995
drizzle/meta/0032_snapshot.json
Normal file
2995
drizzle/meta/0032_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -218,6 +218,13 @@
|
|||||||
"when": 1782051007412,
|
"when": 1782051007412,
|
||||||
"tag": "0031_lame_cerise",
|
"tag": "0031_lame_cerise",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 32,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1782569103402,
|
||||||
|
"tag": "0032_bumpy_spencer_smythe",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|||||||
filters: searchFilters,
|
filters: searchFilters,
|
||||||
slugMaps,
|
slugMaps,
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
|
hideAnticipatedInstallments:
|
||||||
|
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const transactionsPage = await fetchAccountTransactionsPage(
|
const transactionsPage = await fetchAccountTransactionsPage(
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|||||||
filters: searchFilters,
|
filters: searchFilters,
|
||||||
slugMaps,
|
slugMaps,
|
||||||
cardId: card.id,
|
cardId: card.id,
|
||||||
|
hideAnticipatedInstallments:
|
||||||
|
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const transactionRows = await fetchCardTransactions(filters);
|
const transactionRows = await fetchCardTransactions(filters);
|
||||||
|
|||||||
@@ -41,13 +41,17 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|||||||
const periodoParam = getSingleParam(resolvedSearchParams, "periodo");
|
const periodoParam = getSingleParam(resolvedSearchParams, "periodo");
|
||||||
const { period: selectedPeriod } = parsePeriodParam(periodoParam);
|
const { period: selectedPeriod } = parsePeriodParam(periodoParam);
|
||||||
|
|
||||||
const [detail, filterSources, estabelecimentos, userPreferences] =
|
const [filterSources, estabelecimentos, userPreferences] = await Promise.all([
|
||||||
await Promise.all([
|
fetchTransactionFilterSources(userId),
|
||||||
fetchCategoryDetails(userId, categoryId, selectedPeriod),
|
fetchRecentEstablishments(userId),
|
||||||
fetchTransactionFilterSources(userId),
|
fetchUserPreferences(userId),
|
||||||
fetchRecentEstablishments(userId),
|
]);
|
||||||
fetchUserPreferences(userId),
|
const detail = await fetchCategoryDetails(
|
||||||
]);
|
userId,
|
||||||
|
categoryId,
|
||||||
|
selectedPeriod,
|
||||||
|
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||||
|
);
|
||||||
|
|
||||||
if (!detail) {
|
if (!detail) {
|
||||||
notFound();
|
notFound();
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|||||||
...EMPTY_FILTERS,
|
...EMPTY_FILTERS,
|
||||||
searchFilter: allSearchFilters.searchFilter, // Permitir busca mesmo em modo read-only
|
searchFilter: allSearchFilters.searchFilter, // Permitir busca mesmo em modo read-only
|
||||||
};
|
};
|
||||||
|
const userPreferences = await fetchUserPreferences(userId);
|
||||||
|
|
||||||
let filterSources: Awaited<
|
let filterSources: Awaited<
|
||||||
ReturnType<typeof fetchTransactionFilterSources>
|
ReturnType<typeof fetchTransactionFilterSources>
|
||||||
@@ -163,6 +164,8 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|||||||
filters: searchFilters,
|
filters: searchFilters,
|
||||||
slugMaps,
|
slugMaps,
|
||||||
payerId: pagador.id,
|
payerId: pagador.id,
|
||||||
|
hideAnticipatedInstallments:
|
||||||
|
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const sharesPromise = canEdit
|
const sharesPromise = canEdit
|
||||||
@@ -184,7 +187,6 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|||||||
shareRows,
|
shareRows,
|
||||||
currentUserShare,
|
currentUserShare,
|
||||||
estabelecimentos,
|
estabelecimentos,
|
||||||
userPreferences,
|
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
fetchPayerTransactions(filters),
|
fetchPayerTransactions(filters),
|
||||||
fetchPayerMonthlyBreakdown({
|
fetchPayerMonthlyBreakdown({
|
||||||
@@ -220,7 +222,6 @@ export default async function Page({ params, searchParams }: PageProps) {
|
|||||||
sharesPromise,
|
sharesPromise,
|
||||||
currentUserSharePromise,
|
currentUserSharePromise,
|
||||||
fetchRecentEstablishments(userId),
|
fetchRecentEstablishments(userId),
|
||||||
fetchUserPreferences(userId),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const mappedTransactions = mapTransactionsData(transactionRows);
|
const mappedTransactions = mapTransactionsData(transactionRows);
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ export default async function Page() {
|
|||||||
showTransactionSummary={
|
showTransactionSummary={
|
||||||
userPreferences?.showTransactionSummary ?? true
|
userPreferences?.showTransactionSummary ?? true
|
||||||
}
|
}
|
||||||
|
hideAnticipatedInstallments={
|
||||||
|
userPreferences?.hideAnticipatedInstallments ?? false
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export default async function Page({ searchParams }: PageProps) {
|
|||||||
period: selectedPeriod,
|
period: selectedPeriod,
|
||||||
filters: searchFilters,
|
filters: searchFilters,
|
||||||
slugMaps,
|
slugMaps,
|
||||||
|
hideAnticipatedInstallments:
|
||||||
|
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [transactionsPage, estabelecimentos] = await Promise.all([
|
const [transactionsPage, estabelecimentos] = await Promise.all([
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ export const userPreferences = pgTable("preferencias_usuario", {
|
|||||||
showTransactionSummary: boolean("mostrar_resumo_lancamento")
|
showTransactionSummary: boolean("mostrar_resumo_lancamento")
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(true),
|
.default(true),
|
||||||
|
hideAnticipatedInstallments: boolean("ocultar_parcelas_antecipadas")
|
||||||
|
.notNull()
|
||||||
|
.default(false),
|
||||||
dashboardWidgets: jsonb("dashboard_widgets").$type<{
|
dashboardWidgets: jsonb("dashboard_widgets").$type<{
|
||||||
order: string[];
|
order: string[];
|
||||||
hidden: string[];
|
hidden: string[];
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export async function fetchCategoryDetails(
|
|||||||
userId: string,
|
userId: string,
|
||||||
categoryId: string,
|
categoryId: string,
|
||||||
period: string,
|
period: string,
|
||||||
|
hideAnticipatedInstallments = false,
|
||||||
): Promise<CategoryDetailData | null> {
|
): Promise<CategoryDetailData | null> {
|
||||||
const category = await db.query.categories.findFirst({
|
const category = await db.query.categories.findFirst({
|
||||||
where: and(eq(categories.userId, userId), eq(categories.id, categoryId)),
|
where: and(eq(categories.userId, userId), eq(categories.id, categoryId)),
|
||||||
@@ -63,6 +64,14 @@ export async function fetchCategoryDetails(
|
|||||||
eq(transactions.transactionType, transactionType),
|
eq(transactions.transactionType, transactionType),
|
||||||
eq(transactions.period, period),
|
eq(transactions.period, period),
|
||||||
eq(transactions.payerId, adminPayerId),
|
eq(transactions.payerId, adminPayerId),
|
||||||
|
...(hideAnticipatedInstallments
|
||||||
|
? [
|
||||||
|
or(
|
||||||
|
isNull(transactions.isAnticipated),
|
||||||
|
eq(transactions.isAnticipated, false),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
...(isInvoiceCategory ? [] : [sanitizedNote]),
|
...(isInvoiceCategory ? [] : [sanitizedNote]),
|
||||||
),
|
),
|
||||||
with: {
|
with: {
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ const updatePreferencesSchema = z.object({
|
|||||||
transactionsColumnOrder: z.array(z.string()).nullable(),
|
transactionsColumnOrder: z.array(z.string()).nullable(),
|
||||||
attachmentMaxSizeMb: z.number().int().min(1).max(100),
|
attachmentMaxSizeMb: z.number().int().min(1).max(100),
|
||||||
showTransactionSummary: z.boolean(),
|
showTransactionSummary: z.boolean(),
|
||||||
|
hideAnticipatedInstallments: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
type ResettableUser = {
|
type ResettableUser = {
|
||||||
@@ -584,6 +585,7 @@ export async function updatePreferencesAction(
|
|||||||
transactionsColumnOrder: validated.transactionsColumnOrder,
|
transactionsColumnOrder: validated.transactionsColumnOrder,
|
||||||
attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
|
attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
|
||||||
showTransactionSummary: validated.showTransactionSummary,
|
showTransactionSummary: validated.showTransactionSummary,
|
||||||
|
hideAnticipatedInstallments: validated.hideAnticipatedInstallments,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
})
|
})
|
||||||
.where(eq(schema.userPreferences.userId, session.user.id));
|
.where(eq(schema.userPreferences.userId, session.user.id));
|
||||||
@@ -595,6 +597,7 @@ export async function updatePreferencesAction(
|
|||||||
transactionsColumnOrder: validated.transactionsColumnOrder,
|
transactionsColumnOrder: validated.transactionsColumnOrder,
|
||||||
attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
|
attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
|
||||||
showTransactionSummary: validated.showTransactionSummary,
|
showTransactionSummary: validated.showTransactionSummary,
|
||||||
|
hideAnticipatedInstallments: validated.hideAnticipatedInstallments,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ interface PreferencesFormProps {
|
|||||||
transactionsColumnOrder: string[] | null;
|
transactionsColumnOrder: string[] | null;
|
||||||
attachmentMaxSizeMb: number;
|
attachmentMaxSizeMb: number;
|
||||||
showTransactionSummary: boolean;
|
showTransactionSummary: boolean;
|
||||||
|
hideAnticipatedInstallments: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SortableColumnItem({ id }: { id: string }) {
|
function SortableColumnItem({ id }: { id: string }) {
|
||||||
@@ -87,6 +88,7 @@ export function PreferencesForm({
|
|||||||
transactionsColumnOrder: initialColumnOrder,
|
transactionsColumnOrder: initialColumnOrder,
|
||||||
attachmentMaxSizeMb: initialAttachmentMaxSizeMb,
|
attachmentMaxSizeMb: initialAttachmentMaxSizeMb,
|
||||||
showTransactionSummary: initialShowTransactionSummary,
|
showTransactionSummary: initialShowTransactionSummary,
|
||||||
|
hideAnticipatedInstallments: initialHideAnticipatedInstallments,
|
||||||
}: PreferencesFormProps) {
|
}: PreferencesFormProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
@@ -109,6 +111,8 @@ export function PreferencesForm({
|
|||||||
const [showTransactionSummary, setShowTransactionSummary] = useState(
|
const [showTransactionSummary, setShowTransactionSummary] = useState(
|
||||||
initialShowTransactionSummary,
|
initialShowTransactionSummary,
|
||||||
);
|
);
|
||||||
|
const [hideAnticipatedInstallments, setHideAnticipatedInstallments] =
|
||||||
|
useState(initialHideAnticipatedInstallments);
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
|
useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
|
||||||
@@ -135,6 +139,7 @@ export function PreferencesForm({
|
|||||||
transactionsColumnOrder: columnOrder,
|
transactionsColumnOrder: columnOrder,
|
||||||
attachmentMaxSizeMb,
|
attachmentMaxSizeMb,
|
||||||
showTransactionSummary,
|
showTransactionSummary,
|
||||||
|
hideAnticipatedInstallments,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -198,6 +203,26 @@ export function PreferencesForm({
|
|||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
|
<section className="flex items-center justify-between max-w-md gap-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="hide-anticipated-installments" className="text-sm">
|
||||||
|
Ocultar parcelas antecipadas
|
||||||
|
</Label>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Quando ativo, parcelas já antecipadas não aparecem na tabela de
|
||||||
|
lançamentos.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="hide-anticipated-installments"
|
||||||
|
checked={hideAnticipatedInstallments}
|
||||||
|
onCheckedChange={setHideAnticipatedInstallments}
|
||||||
|
disabled={isPending}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
<section className="space-y-2 max-w-md">
|
<section className="space-y-2 max-w-md">
|
||||||
<Label className="text-sm">Ordem das colunas</Label>
|
<Label className="text-sm">Ordem das colunas</Label>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ interface UserPreferences {
|
|||||||
transactionsColumnOrder: string[] | null;
|
transactionsColumnOrder: string[] | null;
|
||||||
attachmentMaxSizeMb: number;
|
attachmentMaxSizeMb: number;
|
||||||
showTransactionSummary: boolean;
|
showTransactionSummary: boolean;
|
||||||
|
hideAnticipatedInstallments: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ApiToken {
|
interface ApiToken {
|
||||||
@@ -36,6 +37,8 @@ export async function fetchUserPreferences(
|
|||||||
transactionsColumnOrder: schema.userPreferences.transactionsColumnOrder,
|
transactionsColumnOrder: schema.userPreferences.transactionsColumnOrder,
|
||||||
attachmentMaxSizeMb: schema.userPreferences.attachmentMaxSizeMb,
|
attachmentMaxSizeMb: schema.userPreferences.attachmentMaxSizeMb,
|
||||||
showTransactionSummary: schema.userPreferences.showTransactionSummary,
|
showTransactionSummary: schema.userPreferences.showTransactionSummary,
|
||||||
|
hideAnticipatedInstallments:
|
||||||
|
schema.userPreferences.hideAnticipatedInstallments,
|
||||||
})
|
})
|
||||||
.from(schema.userPreferences)
|
.from(schema.userPreferences)
|
||||||
.where(eq(schema.userPreferences.userId, userId))
|
.where(eq(schema.userPreferences.userId, userId))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { fetchAccountTransactions } from "@/features/accounts/statement-queries";
|
import { fetchAccountTransactions } from "@/features/accounts/statement-queries";
|
||||||
|
import { fetchUserPreferences } from "@/features/settings/queries";
|
||||||
import type { TransactionsExportContext } from "@/features/transactions/lib/export-types";
|
import type { TransactionsExportContext } from "@/features/transactions/lib/export-types";
|
||||||
import {
|
import {
|
||||||
buildSluggedFilters,
|
buildSluggedFilters,
|
||||||
@@ -60,7 +61,10 @@ export async function exportTransactionsDataAction(
|
|||||||
try {
|
try {
|
||||||
const userId = await getUserId();
|
const userId = await getUserId();
|
||||||
const validated = exportTransactionsSchema.parse(input);
|
const validated = exportTransactionsSchema.parse(input);
|
||||||
const filterSources = await fetchTransactionFilterSources(userId);
|
const [filterSources, userPreferences] = await Promise.all([
|
||||||
|
fetchTransactionFilterSources(userId),
|
||||||
|
fetchUserPreferences(userId),
|
||||||
|
]);
|
||||||
const sluggedFilters = buildSluggedFilters(filterSources);
|
const sluggedFilters = buildSluggedFilters(filterSources);
|
||||||
const slugMaps = buildSlugMaps(sluggedFilters);
|
const slugMaps = buildSlugMaps(sluggedFilters);
|
||||||
|
|
||||||
@@ -72,6 +76,8 @@ export async function exportTransactionsDataAction(
|
|||||||
accountId: validated.accountId ?? undefined,
|
accountId: validated.accountId ?? undefined,
|
||||||
cardId: validated.cardId ?? undefined,
|
cardId: validated.cardId ?? undefined,
|
||||||
payerId: validated.payerId ?? undefined,
|
payerId: validated.payerId ?? undefined,
|
||||||
|
hideAnticipatedInstallments:
|
||||||
|
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const rows =
|
const rows =
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ilike,
|
ilike,
|
||||||
inArray,
|
inArray,
|
||||||
isNotNull,
|
isNotNull,
|
||||||
|
isNull,
|
||||||
lte,
|
lte,
|
||||||
or,
|
or,
|
||||||
sql,
|
sql,
|
||||||
@@ -384,6 +385,7 @@ export const buildTransactionWhere = ({
|
|||||||
cardId,
|
cardId,
|
||||||
accountId,
|
accountId,
|
||||||
payerId,
|
payerId,
|
||||||
|
hideAnticipatedInstallments = false,
|
||||||
}: {
|
}: {
|
||||||
userId: string;
|
userId: string;
|
||||||
period: string;
|
period: string;
|
||||||
@@ -392,6 +394,7 @@ export const buildTransactionWhere = ({
|
|||||||
cardId?: string;
|
cardId?: string;
|
||||||
accountId?: string;
|
accountId?: string;
|
||||||
payerId?: string;
|
payerId?: string;
|
||||||
|
hideAnticipatedInstallments?: boolean;
|
||||||
}): SQL[] => {
|
}): SQL[] => {
|
||||||
const where: SQL[] = [eq(transactions.userId, userId)];
|
const where: SQL[] = [eq(transactions.userId, userId)];
|
||||||
|
|
||||||
@@ -421,6 +424,15 @@ export const buildTransactionWhere = ({
|
|||||||
where.push(eq(transactions.payerId, payerId));
|
where.push(eq(transactions.payerId, payerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hideAnticipatedInstallments) {
|
||||||
|
where.push(
|
||||||
|
or(
|
||||||
|
isNull(transactions.isAnticipated),
|
||||||
|
eq(transactions.isAnticipated, false),
|
||||||
|
) as SQL,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (cardId) {
|
if (cardId) {
|
||||||
where.push(eq(transactions.cardId, cardId));
|
where.push(eq(transactions.cardId, cardId));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user