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) { 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; }), });