mirror of
https://github.com/spliit-app/spliit.git
synced 2026-02-15 20:16:12 +01:00
224 lines
6.1 KiB
TypeScript
224 lines
6.1 KiB
TypeScript
import { getPrisma } from '@/lib/prisma'
|
|
import { ExpenseFormValues, GroupFormValues } from '@/lib/schemas'
|
|
import { Expense } from '@prisma/client'
|
|
import { nanoid } from 'nanoid'
|
|
|
|
export function randomId() {
|
|
return nanoid()
|
|
}
|
|
|
|
export async function createGroup(groupFormValues: GroupFormValues) {
|
|
const prisma = await getPrisma()
|
|
return prisma.group.create({
|
|
data: {
|
|
id: randomId(),
|
|
name: groupFormValues.name,
|
|
currency: groupFormValues.currency,
|
|
participants: {
|
|
createMany: {
|
|
data: groupFormValues.participants.map(({ name }) => ({
|
|
id: randomId(),
|
|
name,
|
|
})),
|
|
},
|
|
},
|
|
},
|
|
include: { participants: true },
|
|
})
|
|
}
|
|
|
|
export async function createExpense(
|
|
expenseFormValues: ExpenseFormValues,
|
|
groupId: string,
|
|
): Promise<Expense> {
|
|
const group = await getGroup(groupId)
|
|
if (!group) throw new Error(`Invalid group ID: ${groupId}`)
|
|
|
|
for (const participant of [
|
|
expenseFormValues.paidBy,
|
|
...expenseFormValues.paidFor.map((p) => p.participant),
|
|
]) {
|
|
if (!group.participants.some((p) => p.id === participant))
|
|
throw new Error(`Invalid participant ID: ${participant}`)
|
|
}
|
|
|
|
const prisma = await getPrisma()
|
|
return prisma.expense.create({
|
|
data: {
|
|
id: randomId(),
|
|
groupId,
|
|
amount: expenseFormValues.amount,
|
|
title: expenseFormValues.title,
|
|
paidById: expenseFormValues.paidBy,
|
|
splitMode: expenseFormValues.splitMode,
|
|
paidFor: {
|
|
createMany: {
|
|
data: expenseFormValues.paidFor.map((paidFor) => ({
|
|
participantId: paidFor.participant,
|
|
shares: paidFor.shares,
|
|
})),
|
|
},
|
|
},
|
|
isReimbursement: expenseFormValues.isReimbursement,
|
|
},
|
|
})
|
|
}
|
|
|
|
export async function deleteExpense(expenseId: string) {
|
|
const prisma = await getPrisma()
|
|
await prisma.expense.delete({
|
|
where: { id: expenseId },
|
|
include: { paidFor: true, paidBy: true },
|
|
})
|
|
}
|
|
|
|
export async function getGroupExpensesParticipants(groupId: string) {
|
|
const expenses = await getGroupExpenses(groupId)
|
|
return Array.from(
|
|
new Set(
|
|
expenses.flatMap((e) => [
|
|
e.paidById,
|
|
...e.paidFor.map((pf) => pf.participantId),
|
|
]),
|
|
),
|
|
)
|
|
}
|
|
|
|
export async function getGroups(groupIds: string[]) {
|
|
const prisma = await getPrisma()
|
|
return (
|
|
await prisma.group.findMany({
|
|
where: { id: { in: groupIds } },
|
|
include: { _count: { select: { participants: true } } },
|
|
})
|
|
).map((group) => ({
|
|
...group,
|
|
createdAt: group.createdAt.toISOString(),
|
|
}))
|
|
}
|
|
|
|
export async function updateExpense(
|
|
groupId: string,
|
|
expenseId: string,
|
|
expenseFormValues: ExpenseFormValues,
|
|
) {
|
|
const group = await getGroup(groupId)
|
|
if (!group) throw new Error(`Invalid group ID: ${groupId}`)
|
|
|
|
const existingExpense = await getExpense(groupId, expenseId)
|
|
if (!existingExpense) throw new Error(`Invalid expense ID: ${expenseId}`)
|
|
|
|
for (const participant of [
|
|
expenseFormValues.paidBy,
|
|
...expenseFormValues.paidFor.map((p) => p.participant),
|
|
]) {
|
|
if (!group.participants.some((p) => p.id === participant))
|
|
throw new Error(`Invalid participant ID: ${participant}`)
|
|
}
|
|
|
|
const prisma = await getPrisma()
|
|
return prisma.expense.update({
|
|
where: { id: expenseId },
|
|
data: {
|
|
amount: expenseFormValues.amount,
|
|
title: expenseFormValues.title,
|
|
paidById: expenseFormValues.paidBy,
|
|
splitMode: expenseFormValues.splitMode,
|
|
paidFor: {
|
|
create: expenseFormValues.paidFor
|
|
.filter(
|
|
(p) =>
|
|
!existingExpense.paidFor.some(
|
|
(pp) => pp.participantId === p.participant,
|
|
),
|
|
)
|
|
.map((paidFor) => ({
|
|
participantId: paidFor.participant,
|
|
shares: paidFor.shares,
|
|
})),
|
|
update: expenseFormValues.paidFor.map((paidFor) => ({
|
|
where: {
|
|
expenseId_participantId: {
|
|
expenseId,
|
|
participantId: paidFor.participant,
|
|
},
|
|
},
|
|
data: {
|
|
shares: paidFor.shares,
|
|
},
|
|
})),
|
|
deleteMany: existingExpense.paidFor.filter(
|
|
(paidFor) =>
|
|
!expenseFormValues.paidFor.some(
|
|
(pf) => pf.participant === paidFor.participantId,
|
|
),
|
|
),
|
|
},
|
|
isReimbursement: expenseFormValues.isReimbursement,
|
|
},
|
|
})
|
|
}
|
|
|
|
export async function updateGroup(
|
|
groupId: string,
|
|
groupFormValues: GroupFormValues,
|
|
) {
|
|
const existingGroup = await getGroup(groupId)
|
|
if (!existingGroup) throw new Error('Invalid group ID')
|
|
|
|
const prisma = await getPrisma()
|
|
return prisma.group.update({
|
|
where: { id: groupId },
|
|
data: {
|
|
name: groupFormValues.name,
|
|
currency: groupFormValues.currency,
|
|
participants: {
|
|
deleteMany: existingGroup.participants.filter(
|
|
(p) => !groupFormValues.participants.some((p2) => p2.id === p.id),
|
|
),
|
|
updateMany: groupFormValues.participants
|
|
.filter((participant) => participant.id !== undefined)
|
|
.map((participant) => ({
|
|
where: { id: participant.id },
|
|
data: {
|
|
name: participant.name,
|
|
},
|
|
})),
|
|
createMany: {
|
|
data: groupFormValues.participants
|
|
.filter((participant) => participant.id === undefined)
|
|
.map((participant) => ({
|
|
id: randomId(),
|
|
name: participant.name,
|
|
})),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
export async function getGroup(groupId: string) {
|
|
const prisma = await getPrisma()
|
|
return prisma.group.findUnique({
|
|
where: { id: groupId },
|
|
include: { participants: true },
|
|
})
|
|
}
|
|
|
|
export async function getGroupExpenses(groupId: string) {
|
|
const prisma = await getPrisma()
|
|
return prisma.expense.findMany({
|
|
where: { groupId },
|
|
include: { paidFor: { include: { participant: true } }, paidBy: true },
|
|
orderBy: { createdAt: 'desc' },
|
|
})
|
|
}
|
|
|
|
export async function getExpense(groupId: string, expenseId: string) {
|
|
const prisma = await getPrisma()
|
|
return prisma.expense.findUnique({
|
|
where: { id: expenseId },
|
|
include: { paidBy: true, paidFor: true },
|
|
})
|
|
}
|