Files
openmonetis/src/features/payers/detail-queries.ts
Felipe Coutinho b14f487824 feat(transactions): edição cooperativa e visibilidade de divisões
Adiciona splitGroupId para vincular as duas shares de um lançamento
dividido (schema + índice + migration 0026). Habilita:

- Edição de par dividido com escolha de escopo (apenas este lado ou
  ambos) via novo SplitPairDialog e updateTransactionSplitPairAction
- Filtro "Somente divididos" (isDivided) na tabela de lançamentos
- Visibilidade de anexos para pessoas com acesso compartilhado via
  payerShares; upload e detach em massa expandem para shares irmãs
- Cópia independente de anexos no fluxo "Importar para Minha Conta"
  (novo fileKey, novo userId, S3 CopyObject) com seção read-only
  "Anexos que serão copiados" no dialog de importação
- Ícone de clipe na tabela de lançamentos da página da pessoa via
  EXISTS em fetchPagadorLancamentos

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 14:45:35 +00:00

102 lines
2.5 KiB
TypeScript

import { and, desc, eq, type SQL, sql } from "drizzle-orm";
import {
cards,
categories,
financialAccounts,
payerShares,
payers,
transactionAttachments,
transactions,
user as usersTable,
} from "@/db/schema";
import { db } from "@/shared/lib/db";
export type ShareData = {
id: string;
userId: string;
name: string;
email: string;
createdAt: string;
};
export async function fetchPayerShares(payerId: string): Promise<ShareData[]> {
const shareRows = await db
.select({
id: payerShares.id,
sharedWithUserId: payerShares.sharedWithUserId,
createdAt: payerShares.createdAt,
userName: usersTable.name,
userEmail: usersTable.email,
})
.from(payerShares)
.innerJoin(usersTable, eq(payerShares.sharedWithUserId, usersTable.id))
.where(eq(payerShares.payerId, payerId));
return shareRows.map((share) => ({
id: share.id,
userId: share.sharedWithUserId,
name: share.userName ?? "Usuário",
email: share.userEmail ?? "email não informado",
createdAt: share.createdAt?.toISOString() ?? new Date().toISOString(),
}));
}
export async function fetchCurrentUserShare(
payerId: string,
userId: string,
): Promise<{ id: string; createdAt: string } | null> {
const shareRow = await db.query.payerShares.findFirst({
columns: {
id: true,
createdAt: true,
},
where: and(
eq(payerShares.payerId, payerId),
eq(payerShares.sharedWithUserId, userId),
),
});
if (!shareRow) {
return null;
}
return {
id: shareRow.id,
createdAt: shareRow.createdAt?.toISOString() ?? new Date().toISOString(),
};
}
export async function fetchPagadorLancamentos(filters: SQL[]) {
const transactionRows = await db
.select({
transaction: transactions,
payer: payers,
financialAccount: financialAccounts,
card: cards,
category: categories,
hasAttachments: sql<boolean>`EXISTS (
SELECT 1 FROM ${transactionAttachments}
WHERE ${transactionAttachments.transactionId} = ${transactions.id}
)`,
})
.from(transactions)
.leftJoin(payers, eq(transactions.payerId, payers.id))
.leftJoin(
financialAccounts,
eq(transactions.accountId, financialAccounts.id),
)
.leftJoin(cards, eq(transactions.cardId, cards.id))
.leftJoin(categories, eq(transactions.categoryId, categories.id))
.where(and(...filters))
.orderBy(desc(transactions.purchaseDate), desc(transactions.createdAt));
return transactionRows.map((row) => ({
...row.transaction,
payer: row.payer,
financialAccount: row.financialAccount,
card: row.card,
category: row.category,
hasAttachments: row.hasAttachments,
}));
}