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 = { [key: string]: unknown } export type TransactionFilters = { search?: string dateFrom?: string dateTo?: string ordering?: string categoryCode?: string projectCode?: 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 } }, { merchant: { contains: filters.search } }, { description: { contains: filters.search } }, { note: { contains: filters.search } }, { text: { contains: filters.search } }, ] } 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.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 createTransaction = async (userId: string, data: TransactionData): Promise => { const { standard, extra } = await splitTransactionDataExtraFields(data, userId) return await prisma.transaction.create({ data: { ...standard, extra: extra, 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, }, }) } 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[]) { 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: Omit, "extra"> = {} const extra: Record = {} Object.entries(data).forEach(([key, value]) => { const fieldDef = fieldMap[key] if (fieldDef) { if (fieldDef.isExtra) { extra[key] = value } else { standard[key as keyof Omit] = value as any } } }) return { standard, extra: extra as Prisma.InputJsonValue } }