Files
TaxHacker/models/transactions.ts
2025-05-03 10:23:13 +02:00

190 lines
4.9 KiB
TypeScript

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<Transaction | null> => {
return await prisma.transaction.findUnique({
where: { id, userId },
include: {
category: true,
project: true,
},
})
})
export const createTransaction = async (userId: string, data: TransactionData): Promise<Transaction> => {
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<Transaction> => {
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<Transaction> => {
return await prisma.transaction.update({
where: { id, userId },
data: { files },
})
}
export const deleteTransaction = async (id: string, userId: string): Promise<Transaction | undefined> => {
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<string, Field>
)
const standard: Omit<Partial<Transaction>, "extra"> = {}
const extra: Record<string, unknown> = {}
Object.entries(data).forEach(([key, value]) => {
const fieldDef = fieldMap[key]
if (fieldDef) {
if (fieldDef.isExtra) {
extra[key] = value
} else {
standard[key as keyof Omit<Transaction, "extra">] = value as any
}
}
})
return { standard, extra: extra as Prisma.InputJsonValue }
}