117 lines
3.1 KiB
TypeScript

import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import {
expenses,
expenseSplits,
friendships,
type ExpenseSplit,
} from "@/server/db/schema";
import { expenseSchema, expenseSplitSchema } from "@/lib/validations/expense";
import { and, eq, inArray, not, notInArray, or } from "drizzle-orm";
function restructureExpenseSplits(expenseSplits: Array<ExpenseSplit>) {
const expensesMap = new Map();
expenseSplits.forEach((split) => {
const expenseId = split.expenseId;
const expense = split.expense;
if (!expensesMap.has(expenseId)) {
expensesMap.set(expenseId, {
...expense,
splits: [],
});
}
expensesMap.get(expenseId).splits.push(split);
});
return Array.from(expensesMap.values());
}
export const expenseRouter = createTRPCRouter({
getAll: protectedProcedure.query(async ({ ctx }) => {
const splits = await ctx.db.query.expenseSplits.findMany({
where: or(
eq(expenseSplits.owedFromId, ctx.auth.userId),
eq(expenseSplits.owedToId, ctx.auth.userId)
),
with: {
owedFrom: true,
owedTo: true,
expense: true,
},
});
const expenses = restructureExpenseSplits(splits);
return expenses;
}),
searchParticipants: protectedProcedure
.input(z.object({ search: z.string(), excludedIds: z.array(z.string()) }))
.query(async ({ ctx, input }) => {
const userId = ctx.auth.userId;
const friendResult = await ctx.db.query.friendships.findMany({
where: and(
or(
eq(friendships.userOneId, userId),
eq(friendships.userTwoId, userId)
),
notInArray(friendships.userOneId, input.excludedIds),
notInArray(friendships.userTwoId, input.excludedIds)
),
with: {
userOne: true,
userTwo: true,
},
});
const friends =
friendResult?.map(({ userOne, userTwo }) =>
ctx.auth.userId === userOne.id ? userTwo : userOne
) ?? [];
return {
friends,
groups: [],
};
}),
// mutations
create: protectedProcedure
.input(
z.object({
expense: expenseSchema,
debs: z.array(
expenseSplitSchema.pick({
owedFromId: true,
owedToId: true,
amount: true,
})
),
})
)
.mutation(async ({ ctx, input }) => {
const [expense] = await ctx.db
.insert(expenses)
.values({
createdById: ctx.auth.userId,
...input.expense,
amount: input.expense.amount.toString(),
})
.returning({ id: expenses.id });
if (!expense?.id?.length) throw new Error("Expense cant get created");
await ctx.db
.insert(expenseSplits)
.values(
input.debs.map((deb) => ({
...deb,
amount: deb.amount.toString(),
expenseId: expense.id,
status: "unpaid",
}))
)
.returning({ id: expenseSplits.id });
return expense;
}),
});