import { prisma } from "@/lib/db" import { Field, Prisma, Transaction } from "@/prisma/client" import { cache } from "react" import { getFields } from "./fields" import { deleteFile } from "./files" export type TransactionData = { name?: string | null description?: string | null merchant?: string | null total?: number | null currencyCode?: string | null convertedTotal?: number | null convertedCurrencyCode?: string | null type?: string | null items?: TransactionData[] | undefined note?: string | null files?: string[] | undefined extra?: Record categoryCode?: string | null projectCode?: string | null issuedAt?: Date | string | null text?: string | null [key: string]: unknown } export type TransactionFilters = { search?: string dateFrom?: string dateTo?: string ordering?: string categoryCode?: string projectCode?: string type?: string page?: number } export type TransactionPagination = { limit: number offset: number } export const getTransactions = cache( async ( userId: string, filters?: TransactionFilters, pagination?: TransactionPagination ): Promise<{ transactions: Transaction[] total: number }> => { const where: Prisma.TransactionWhereInput = { userId } let orderBy: Prisma.TransactionOrderByWithRelationInput = { issuedAt: "desc" } if (filters) { if (filters.search) { where.OR = [ { name: { contains: filters.search, mode: "insensitive" } }, { merchant: { contains: filters.search, mode: "insensitive" } }, { description: { contains: filters.search, mode: "insensitive" } }, { note: { contains: filters.search, mode: "insensitive" } }, { text: { contains: filters.search, mode: "insensitive" } }, ] } if (filters.dateFrom || filters.dateTo) { where.issuedAt = { gte: filters.dateFrom ? new Date(filters.dateFrom) : undefined, lte: filters.dateTo ? new Date(filters.dateTo) : undefined, } } if (filters.categoryCode) { where.categoryCode = filters.categoryCode } if (filters.projectCode) { where.projectCode = filters.projectCode } if (filters.type) { where.type = filters.type } if (filters.ordering) { const isDesc = filters.ordering.startsWith("-") const field = isDesc ? filters.ordering.slice(1) : filters.ordering orderBy = { [field]: isDesc ? "desc" : "asc" } } } if (pagination) { const total = await prisma.transaction.count({ where }) const transactions = await prisma.transaction.findMany({ where, include: { category: true, project: true, }, orderBy, take: pagination?.limit, skip: pagination?.offset, }) return { transactions, total } } else { const transactions = await prisma.transaction.findMany({ where, include: { category: true, project: true, }, orderBy, }) return { transactions, total: transactions.length } } } ) export const getTransactionById = cache(async (id: string, userId: string): Promise => { return await prisma.transaction.findUnique({ where: { id, userId }, include: { category: true, project: true, }, }) }) export const getTransactionsByFileId = cache(async (fileId: string, userId: string): Promise => { return await prisma.transaction.findMany({ where: { files: { array_contains: [fileId] }, userId }, }) }) export const createTransaction = async (userId: string, data: TransactionData): Promise => { const { standard, extra } = await splitTransactionDataExtraFields(data, userId) return await prisma.transaction.create({ data: { ...standard, extra: extra, items: data.items as Prisma.InputJsonValue, userId, }, }) } export const updateTransaction = async (id: string, userId: string, data: TransactionData): Promise => { const { standard, extra } = await splitTransactionDataExtraFields(data, userId) return await prisma.transaction.update({ where: { id, userId }, data: { ...standard, extra: extra, items: data.items ? (data.items as Prisma.InputJsonValue) : [], }, }) } export const updateTransactionFiles = async (id: string, userId: string, files: string[]): Promise => { return await prisma.transaction.update({ where: { id, userId }, data: { files }, }) } export const deleteTransaction = async (id: string, userId: string): Promise => { const transaction = await getTransactionById(id, userId) if (transaction) { const files = Array.isArray(transaction.files) ? transaction.files : [] for (const fileId of files as string[]) { if ((await getTransactionsByFileId(fileId, userId)).length <= 1) { await deleteFile(fileId, userId) } } return await prisma.transaction.delete({ where: { id, userId }, }) } } export const bulkDeleteTransactions = async (ids: string[], userId: string) => { return await prisma.transaction.deleteMany({ where: { id: { in: ids }, userId }, }) } const splitTransactionDataExtraFields = async ( data: TransactionData, userId: string ): Promise<{ standard: TransactionData; extra: Prisma.InputJsonValue }> => { const fields = await getFields(userId) const fieldMap = fields.reduce( (acc, field) => { acc[field.code] = field return acc }, {} as Record ) const standard: TransactionData = {} const extra: Record = {} Object.entries(data).forEach(([key, value]) => { const fieldDef = fieldMap[key] if (fieldDef) { if (fieldDef.isExtra) { extra[key] = value } else { standard[key] = value } } }) return { standard, extra: extra as Prisma.InputJsonValue } }