mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
refactor(core): move app para src e padroniza estrutura
This commit is contained in:
941
src/db/schema.ts
Normal file
941
src/db/schema.ts
Normal file
@@ -0,0 +1,941 @@
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
date,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
numeric,
|
||||
pgTable,
|
||||
smallint,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
export const user = pgTable("user", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
email: text("email").notNull().unique(),
|
||||
emailVerified: boolean("emailVerified").notNull(),
|
||||
image: text("image"),
|
||||
createdAt: timestamp("createdAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
updatedAt: timestamp("updatedAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
});
|
||||
|
||||
export const account = pgTable("account", {
|
||||
id: text("id").primaryKey(),
|
||||
accountId: text("accountId").notNull(),
|
||||
providerId: text("providerId").notNull(),
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
accessToken: text("accessToken"),
|
||||
refreshToken: text("refreshToken"),
|
||||
idToken: text("idToken"),
|
||||
accessTokenExpiresAt: timestamp("accessTokenExpiresAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
refreshTokenExpiresAt: timestamp("refreshTokenExpiresAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
scope: text("scope"),
|
||||
password: text("password"),
|
||||
createdAt: timestamp("createdAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
updatedAt: timestamp("updatedAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
});
|
||||
|
||||
export const session = pgTable("session", {
|
||||
id: text("id").primaryKey(),
|
||||
expiresAt: timestamp("expiresAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
token: text("token").notNull().unique(),
|
||||
createdAt: timestamp("createdAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
updatedAt: timestamp("updatedAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
ipAddress: text("ipAddress"),
|
||||
userAgent: text("userAgent"),
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const verification = pgTable("verification", {
|
||||
id: text("id").primaryKey(),
|
||||
identifier: text("identifier").notNull(),
|
||||
value: text("value").notNull(),
|
||||
expiresAt: timestamp("expiresAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
createdAt: timestamp("createdAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
updatedAt: timestamp("updatedAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
});
|
||||
|
||||
// ===================== PASSKEY (WebAuthn) =====================
|
||||
|
||||
export const passkey = pgTable("passkey", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text("name"),
|
||||
publicKey: text("publicKey").notNull(),
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
credentialID: text("credentialID").notNull(),
|
||||
counter: integer("counter").notNull(),
|
||||
deviceType: text("deviceType").notNull(),
|
||||
backedUp: boolean("backedUp").notNull(),
|
||||
transports: text("transports"),
|
||||
aaguid: text("aaguid"),
|
||||
createdAt: timestamp("createdAt", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
});
|
||||
|
||||
export const preferenciasUsuario = pgTable("preferencias_usuario", {
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.unique()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
extratoNoteAsColumn: boolean("extrato_note_as_column")
|
||||
.notNull()
|
||||
.default(false),
|
||||
systemFont: text("system_font").notNull().default("ai-sans"),
|
||||
moneyFont: text("money_font").notNull().default("ai-sans"),
|
||||
lancamentosColumnOrder: jsonb("lancamentos_column_order").$type<
|
||||
string[] | null
|
||||
>(),
|
||||
dashboardWidgets: jsonb("dashboard_widgets").$type<{
|
||||
order: string[];
|
||||
hidden: string[];
|
||||
}>(),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.default(sql`now()`),
|
||||
updatedAt: timestamp("updated_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.default(sql`now()`),
|
||||
});
|
||||
|
||||
// ===================== PUBLIC TABLES =====================
|
||||
|
||||
export const contas = pgTable(
|
||||
"contas",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
name: text("nome").notNull(),
|
||||
accountType: text("tipo_conta").notNull(),
|
||||
note: text("anotacao"),
|
||||
status: text("status").notNull(),
|
||||
logo: text("logo").notNull(),
|
||||
initialBalance: numeric("saldo_inicial", { precision: 12, scale: 2 })
|
||||
.notNull()
|
||||
.default("0"),
|
||||
excludeFromBalance: boolean("excluir_do_saldo").notNull().default(false),
|
||||
excludeInitialBalanceFromIncome: boolean("excluir_saldo_inicial_receitas")
|
||||
.notNull()
|
||||
.default(false),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
userIdStatusIdx: index("contas_user_id_status_idx").on(
|
||||
table.userId,
|
||||
table.status,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const categorias = pgTable(
|
||||
"categorias",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
name: text("nome").notNull(),
|
||||
type: text("tipo").notNull(),
|
||||
icon: text("icone"),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
},
|
||||
(table) => ({
|
||||
userIdTypeIdx: index("categorias_user_id_type_idx").on(
|
||||
table.userId,
|
||||
table.type,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const pagadores = pgTable(
|
||||
"pagadores",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
name: text("nome").notNull(),
|
||||
email: text("email"),
|
||||
avatarUrl: text("avatar_url"),
|
||||
status: text("status").notNull(),
|
||||
note: text("anotacao"),
|
||||
role: text("role"),
|
||||
isAutoSend: boolean("is_auto_send").notNull().default(false),
|
||||
shareCode: text("share_code")
|
||||
.notNull()
|
||||
.default(sql`substr(encode(gen_random_bytes(24), 'base64'), 1, 24)`),
|
||||
lastMailAt: timestamp("last_mail", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
},
|
||||
(table) => ({
|
||||
uniqueShareCode: uniqueIndex("pagadores_share_code_key").on(
|
||||
table.shareCode,
|
||||
),
|
||||
userIdStatusIdx: index("pagadores_user_id_status_idx").on(
|
||||
table.userId,
|
||||
table.status,
|
||||
),
|
||||
userIdRoleIdx: index("pagadores_user_id_role_idx").on(
|
||||
table.userId,
|
||||
table.role,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const compartilhamentosPagador = pgTable(
|
||||
"compartilhamentos_pagador",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
pagadorId: uuid("pagador_id")
|
||||
.notNull()
|
||||
.references(() => pagadores.id, { onDelete: "cascade" }),
|
||||
sharedWithUserId: text("shared_with_user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
permission: text("permission").notNull().default("read"),
|
||||
createdByUserId: text("created_by_user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
uniqueCompartilhamentoPagador: uniqueIndex(
|
||||
"compartilhamentos_pagador_unique",
|
||||
).on(table.pagadorId, table.sharedWithUserId),
|
||||
}),
|
||||
);
|
||||
|
||||
export const cartoes = pgTable(
|
||||
"cartoes",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
name: text("nome").notNull(),
|
||||
closingDay: text("dt_fechamento").notNull(),
|
||||
dueDay: text("dt_vencimento").notNull(),
|
||||
note: text("anotacao"),
|
||||
limit: numeric("limite", { precision: 10, scale: 2 }),
|
||||
brand: text("bandeira"),
|
||||
logo: text("logo"),
|
||||
status: text("status").notNull(),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
contaId: uuid("conta_id")
|
||||
.notNull()
|
||||
.references(() => contas.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
},
|
||||
(table) => ({
|
||||
userIdStatusIdx: index("cartoes_user_id_status_idx").on(
|
||||
table.userId,
|
||||
table.status,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const faturas = pgTable(
|
||||
"faturas",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
paymentStatus: text("status_pagamento"),
|
||||
period: text("periodo"),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
cartaoId: uuid("cartao_id").references(() => cartoes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
},
|
||||
(table) => ({
|
||||
userIdPeriodIdx: index("faturas_user_id_period_idx").on(
|
||||
table.userId,
|
||||
table.period,
|
||||
),
|
||||
cartaoIdPeriodIdx: index("faturas_cartao_id_period_idx").on(
|
||||
table.cartaoId,
|
||||
table.period,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const orcamentos = pgTable(
|
||||
"orcamentos",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
amount: numeric("valor", { precision: 10, scale: 2 }).notNull(),
|
||||
period: text("periodo").notNull(),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
categoriaId: uuid("categoria_id").references(() => categorias.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
},
|
||||
(table) => ({
|
||||
userIdPeriodIdx: index("orcamentos_user_id_period_idx").on(
|
||||
table.userId,
|
||||
table.period,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const anotacoes = pgTable("anotacoes", {
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
title: text("titulo"),
|
||||
description: text("descricao"),
|
||||
type: text("tipo").notNull().default("nota"), // "nota" ou "tarefa"
|
||||
tasks: text("tasks"), // JSON stringificado com array de tarefas
|
||||
arquivada: boolean("arquivada").notNull().default(false),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const insightsSalvos = pgTable(
|
||||
"insights_salvos",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
period: text("period").notNull(),
|
||||
modelId: text("model_id").notNull(),
|
||||
data: text("data").notNull(), // JSON stringificado com as análises
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
updatedAt: timestamp("updated_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
userPeriodIdx: uniqueIndex("insights_salvos_user_period_idx").on(
|
||||
table.userId,
|
||||
table.period,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
// ===================== OPENMONETIS COMPANION =====================
|
||||
|
||||
export const tokensApi = pgTable(
|
||||
"tokens_api",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
name: text("name").notNull(), // Ex: "Meu Samsung Galaxy"
|
||||
tokenHash: text("token_hash").notNull(), // SHA-256 do token
|
||||
tokenPrefix: text("token_prefix").notNull(), // Primeiros 8 chars (display)
|
||||
lastUsedAt: timestamp("last_used_at", { mode: "date", withTimezone: true }),
|
||||
lastUsedIp: text("last_used_ip"),
|
||||
expiresAt: timestamp("expires_at", { mode: "date", withTimezone: true }),
|
||||
revokedAt: timestamp("revoked_at", { mode: "date", withTimezone: true }),
|
||||
createdAt: timestamp("created_at", { mode: "date", withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
userIdIdx: index("tokens_api_user_id_idx").on(table.userId),
|
||||
tokenHashIdx: uniqueIndex("tokens_api_token_hash_idx").on(table.tokenHash),
|
||||
}),
|
||||
);
|
||||
|
||||
export const preLancamentos = pgTable(
|
||||
"pre_lancamentos",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
|
||||
// Informações da fonte
|
||||
sourceApp: text("source_app").notNull(), // Ex: "com.nu.production"
|
||||
sourceAppName: text("source_app_name"), // Ex: "Nubank"
|
||||
|
||||
// Dados originais da notificação
|
||||
originalTitle: text("original_title"),
|
||||
originalText: text("original_text").notNull(),
|
||||
notificationTimestamp: timestamp("notification_timestamp", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}).notNull(),
|
||||
|
||||
// Dados parseados (editáveis pelo usuário antes de processar)
|
||||
parsedName: text("parsed_name"), // Nome do estabelecimento
|
||||
parsedAmount: numeric("parsed_amount", { precision: 12, scale: 2 }),
|
||||
|
||||
// Status de processamento
|
||||
status: text("status").notNull().default("pending"), // pending, processed, discarded
|
||||
|
||||
// Referência ao lançamento criado (se processado)
|
||||
lancamentoId: uuid("lancamento_id").references(() => lancamentos.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
|
||||
// Metadados de processamento
|
||||
processedAt: timestamp("processed_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
discardedAt: timestamp("discarded_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
}),
|
||||
|
||||
// Timestamps
|
||||
createdAt: timestamp("created_at", { mode: "date", withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { mode: "date", withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
userIdStatusIdx: index("pre_lancamentos_user_id_status_idx").on(
|
||||
table.userId,
|
||||
table.status,
|
||||
),
|
||||
userIdCreatedAtIdx: index("pre_lancamentos_user_id_created_at_idx").on(
|
||||
table.userId,
|
||||
table.createdAt,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const antecipacoesParcelas = pgTable(
|
||||
"antecipacoes_parcelas",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
seriesId: uuid("series_id").notNull(),
|
||||
anticipationPeriod: text("periodo_antecipacao").notNull(),
|
||||
anticipationDate: date("data_antecipacao", { mode: "date" }).notNull(),
|
||||
anticipatedInstallmentIds: jsonb("parcelas_antecipadas")
|
||||
.notNull()
|
||||
.$type<string[]>(),
|
||||
totalAmount: numeric("valor_total", { precision: 12, scale: 2 }).notNull(),
|
||||
installmentCount: smallint("qtde_parcelas").notNull(),
|
||||
discount: numeric("desconto", { precision: 12, scale: 2 })
|
||||
.notNull()
|
||||
.default("0"),
|
||||
lancamentoId: uuid("lancamento_id")
|
||||
.notNull()
|
||||
.references(() => lancamentos.id, { onDelete: "cascade" }),
|
||||
pagadorId: uuid("pagador_id").references(() => pagadores.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
categoriaId: uuid("categoria_id").references(() => categorias.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
note: text("anotacao"),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
seriesIdIdx: index("antecipacoes_parcelas_series_id_idx").on(
|
||||
table.seriesId,
|
||||
),
|
||||
userIdIdx: index("antecipacoes_parcelas_user_id_idx").on(table.userId),
|
||||
}),
|
||||
);
|
||||
|
||||
// ===================== RECURRING SERIES =====================
|
||||
|
||||
export type RecurringSeriesTemplate = {
|
||||
name: string;
|
||||
amount: string;
|
||||
transactionType: string;
|
||||
paymentMethod: string;
|
||||
categoriaId: string | null;
|
||||
contaId: string | null;
|
||||
cartaoId: string | null;
|
||||
pagadorId: string | null;
|
||||
note: string | null;
|
||||
condition: string;
|
||||
};
|
||||
|
||||
export const recurringSeries = pgTable(
|
||||
"recurring_series",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
status: text("status").notNull().default("active"), // "active" | "paused" | "cancelled"
|
||||
dayOfMonth: smallint("day_of_month").notNull(),
|
||||
lastGeneratedPeriod: text("last_generated_period").notNull(), // YYYY-MM
|
||||
templateData: jsonb("template_data")
|
||||
.notNull()
|
||||
.$type<RecurringSeriesTemplate>(),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
updatedAt: timestamp("updated_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
userIdStatusIdx: index("recurring_series_user_id_status_idx").on(
|
||||
table.userId,
|
||||
table.status,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
// ===================== LANCAMENTOS =====================
|
||||
|
||||
export const lancamentos = pgTable(
|
||||
"lancamentos",
|
||||
{
|
||||
id: uuid("id").primaryKey().default(sql`gen_random_uuid()`),
|
||||
condition: text("condicao").notNull(),
|
||||
name: text("nome").notNull(),
|
||||
paymentMethod: text("forma_pagamento").notNull(),
|
||||
note: text("anotacao"),
|
||||
amount: numeric("valor", { precision: 12, scale: 2 }).notNull(),
|
||||
purchaseDate: date("data_compra", { mode: "date" }).notNull(),
|
||||
transactionType: text("tipo_transacao").notNull(),
|
||||
installmentCount: smallint("qtde_parcela"),
|
||||
period: text("periodo").notNull(),
|
||||
currentInstallment: smallint("parcela_atual"),
|
||||
recurrenceCount: integer("qtde_recorrencia"),
|
||||
dueDate: date("data_vencimento", { mode: "date" }),
|
||||
boletoPaymentDate: date("dt_pagamento_boleto", { mode: "date" }),
|
||||
isSettled: boolean("realizado").default(false),
|
||||
isDivided: boolean("dividido").default(false),
|
||||
isAnticipated: boolean("antecipado").default(false),
|
||||
anticipationId: uuid("antecipacao_id").references(
|
||||
() => antecipacoesParcelas.id,
|
||||
{ onDelete: "set null" },
|
||||
),
|
||||
createdAt: timestamp("created_at", {
|
||||
mode: "date",
|
||||
withTimezone: true,
|
||||
})
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
cartaoId: uuid("cartao_id").references(() => cartoes.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
contaId: uuid("conta_id").references(() => contas.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
categoriaId: uuid("categoria_id").references(() => categorias.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
pagadorId: uuid("pagador_id").references(() => pagadores.id, {
|
||||
onDelete: "cascade",
|
||||
onUpdate: "cascade",
|
||||
}),
|
||||
seriesId: uuid("series_id"),
|
||||
transferId: uuid("transfer_id"),
|
||||
recurringSeriesId: uuid("recurring_series_id").references(
|
||||
() => recurringSeries.id,
|
||||
{ onDelete: "set null" },
|
||||
),
|
||||
},
|
||||
(table) => ({
|
||||
// Índice composto mais importante: userId + period (usado em quase todas as queries do dashboard)
|
||||
userIdPeriodIdx: index("lancamentos_user_id_period_idx").on(
|
||||
table.userId,
|
||||
table.period,
|
||||
),
|
||||
// Índice composto userId + period + transactionType (cobre maioria das queries do dashboard)
|
||||
userIdPeriodTypeIdx: index("lancamentos_user_id_period_type_idx").on(
|
||||
table.userId,
|
||||
table.period,
|
||||
table.transactionType,
|
||||
),
|
||||
// Índice para queries por pagador + period (invoice/breakdown queries)
|
||||
pagadorIdPeriodIdx: index("lancamentos_pagador_id_period_idx").on(
|
||||
table.pagadorId,
|
||||
table.period,
|
||||
),
|
||||
// Índice para queries ordenadas por data de compra
|
||||
userIdPurchaseDateIdx: index("lancamentos_user_id_purchase_date_idx").on(
|
||||
table.userId,
|
||||
table.purchaseDate,
|
||||
),
|
||||
// Índice para buscar parcelas de uma série
|
||||
seriesIdIdx: index("lancamentos_series_id_idx").on(table.seriesId),
|
||||
// Índice para buscar transferências relacionadas
|
||||
transferIdIdx: index("lancamentos_transfer_id_idx").on(table.transferId),
|
||||
// Índice para filtrar por condição (aberto, realizado, cancelado)
|
||||
userIdConditionIdx: index("lancamentos_user_id_condition_idx").on(
|
||||
table.userId,
|
||||
table.condition,
|
||||
),
|
||||
// Índice para queries de cartão específico
|
||||
cartaoIdPeriodIdx: index("lancamentos_cartao_id_period_idx").on(
|
||||
table.cartaoId,
|
||||
table.period,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const userRelations = relations(user, ({ many, one }) => ({
|
||||
accounts: many(account),
|
||||
sessions: many(session),
|
||||
anotacoes: many(anotacoes),
|
||||
cartoes: many(cartoes),
|
||||
categorias: many(categorias),
|
||||
contas: many(contas),
|
||||
faturas: many(faturas),
|
||||
lancamentos: many(lancamentos),
|
||||
orcamentos: many(orcamentos),
|
||||
pagadores: many(pagadores),
|
||||
antecipacoesParcelas: many(antecipacoesParcelas),
|
||||
tokensApi: many(tokensApi),
|
||||
preLancamentos: many(preLancamentos),
|
||||
recurringSeries: many(recurringSeries),
|
||||
}));
|
||||
|
||||
export const accountRelations = relations(account, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [account.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const sessionRelations = relations(session, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [session.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const contasRelations = relations(contas, ({ one, many }) => ({
|
||||
user: one(user, {
|
||||
fields: [contas.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
cartoes: many(cartoes),
|
||||
lancamentos: many(lancamentos),
|
||||
}));
|
||||
|
||||
export const categoriasRelations = relations(categorias, ({ one, many }) => ({
|
||||
user: one(user, {
|
||||
fields: [categorias.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
lancamentos: many(lancamentos),
|
||||
orcamentos: many(orcamentos),
|
||||
}));
|
||||
|
||||
export const pagadoresRelations = relations(pagadores, ({ one, many }) => ({
|
||||
user: one(user, {
|
||||
fields: [pagadores.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
lancamentos: many(lancamentos),
|
||||
compartilhamentos: many(compartilhamentosPagador),
|
||||
}));
|
||||
|
||||
export const compartilhamentosPagadorRelations = relations(
|
||||
compartilhamentosPagador,
|
||||
({ one }) => ({
|
||||
pagador: one(pagadores, {
|
||||
fields: [compartilhamentosPagador.pagadorId],
|
||||
references: [pagadores.id],
|
||||
}),
|
||||
sharedWithUser: one(user, {
|
||||
fields: [compartilhamentosPagador.sharedWithUserId],
|
||||
references: [user.id],
|
||||
}),
|
||||
createdByUser: one(user, {
|
||||
fields: [compartilhamentosPagador.createdByUserId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export const cartoesRelations = relations(cartoes, ({ one, many }) => ({
|
||||
user: one(user, {
|
||||
fields: [cartoes.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
conta: one(contas, {
|
||||
fields: [cartoes.contaId],
|
||||
references: [contas.id],
|
||||
}),
|
||||
faturas: many(faturas),
|
||||
lancamentos: many(lancamentos),
|
||||
}));
|
||||
|
||||
export const faturasRelations = relations(faturas, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [faturas.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
cartao: one(cartoes, {
|
||||
fields: [faturas.cartaoId],
|
||||
references: [cartoes.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const orcamentosRelations = relations(orcamentos, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [orcamentos.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
categoria: one(categorias, {
|
||||
fields: [orcamentos.categoriaId],
|
||||
references: [categorias.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const anotacoesRelations = relations(anotacoes, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [anotacoes.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const insightsSalvosRelations = relations(insightsSalvos, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [insightsSalvos.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const tokensApiRelations = relations(tokensApi, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [tokensApi.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const preLancamentosRelations = relations(preLancamentos, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [preLancamentos.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
lancamento: one(lancamentos, {
|
||||
fields: [preLancamentos.lancamentoId],
|
||||
references: [lancamentos.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const lancamentosRelations = relations(lancamentos, ({ one }) => ({
|
||||
user: one(user, {
|
||||
fields: [lancamentos.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
cartao: one(cartoes, {
|
||||
fields: [lancamentos.cartaoId],
|
||||
references: [cartoes.id],
|
||||
}),
|
||||
conta: one(contas, {
|
||||
fields: [lancamentos.contaId],
|
||||
references: [contas.id],
|
||||
}),
|
||||
categoria: one(categorias, {
|
||||
fields: [lancamentos.categoriaId],
|
||||
references: [categorias.id],
|
||||
}),
|
||||
pagador: one(pagadores, {
|
||||
fields: [lancamentos.pagadorId],
|
||||
references: [pagadores.id],
|
||||
}),
|
||||
anticipation: one(antecipacoesParcelas, {
|
||||
fields: [lancamentos.anticipationId],
|
||||
references: [antecipacoesParcelas.id],
|
||||
}),
|
||||
recurringSeries: one(recurringSeries, {
|
||||
fields: [lancamentos.recurringSeriesId],
|
||||
references: [recurringSeries.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const recurringSeriesRelations = relations(
|
||||
recurringSeries,
|
||||
({ one, many }) => ({
|
||||
user: one(user, {
|
||||
fields: [recurringSeries.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
lancamentos: many(lancamentos),
|
||||
}),
|
||||
);
|
||||
|
||||
export const antecipacoesParcRelations = relations(
|
||||
antecipacoesParcelas,
|
||||
({ one, many }) => ({
|
||||
user: one(user, {
|
||||
fields: [antecipacoesParcelas.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
lancamento: one(lancamentos, {
|
||||
fields: [antecipacoesParcelas.lancamentoId],
|
||||
references: [lancamentos.id],
|
||||
}),
|
||||
pagador: one(pagadores, {
|
||||
fields: [antecipacoesParcelas.pagadorId],
|
||||
references: [pagadores.id],
|
||||
}),
|
||||
categoria: one(categorias, {
|
||||
fields: [antecipacoesParcelas.categoriaId],
|
||||
references: [categorias.id],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export type User = typeof user.$inferSelect;
|
||||
export type NewUser = typeof user.$inferInsert;
|
||||
export type Account = typeof account.$inferSelect;
|
||||
export type Session = typeof session.$inferSelect;
|
||||
export type Verification = typeof verification.$inferSelect;
|
||||
export type PreferenciasUsuario = typeof preferenciasUsuario.$inferSelect;
|
||||
export type NovasPreferenciasUsuario = typeof preferenciasUsuario.$inferInsert;
|
||||
export type CompartilhamentoPagador =
|
||||
typeof compartilhamentosPagador.$inferSelect;
|
||||
export type Conta = typeof contas.$inferSelect;
|
||||
export type Categoria = typeof categorias.$inferSelect;
|
||||
export type Pagador = typeof pagadores.$inferSelect;
|
||||
export type Cartao = typeof cartoes.$inferSelect;
|
||||
export type Fatura = typeof faturas.$inferSelect;
|
||||
export type Orcamento = typeof orcamentos.$inferSelect;
|
||||
export type Anotacao = typeof anotacoes.$inferSelect;
|
||||
export type InsightSalvo = typeof insightsSalvos.$inferSelect;
|
||||
export type Lancamento = typeof lancamentos.$inferSelect;
|
||||
export type AntecipacaoParcela = typeof antecipacoesParcelas.$inferSelect;
|
||||
export type TokenApi = typeof tokensApi.$inferSelect;
|
||||
export type NovoTokenApi = typeof tokensApi.$inferInsert;
|
||||
export type PreLancamento = typeof preLancamentos.$inferSelect;
|
||||
export type NovoPreLancamento = typeof preLancamentos.$inferInsert;
|
||||
export type RecurringSeries = typeof recurringSeries.$inferSelect;
|
||||
export type NewRecurringSeries = typeof recurringSeries.$inferInsert;
|
||||
Reference in New Issue
Block a user