import { NextRequest, NextResponse } from "next/server"; import { prisma, queryRaw } from "@/lib/db"; import { assignTransactionsToTrip, canAccessTransactions } from "@/lib/queries"; import { getCurrentUser } from "@/lib/auth"; export async function POST(req: NextRequest) { const user = await getCurrentUser(req); if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 403 }); const body = await req.json(); const { action, ids, category, merchant_normalized, splits, tag_id } = body as { action: string; ids: number[]; category?: string; merchant_normalized?: string; splits?: { participant_id: number; share_percent: number }[]; tag_id?: number; }; if (!ids || !Array.isArray(ids) || ids.length === 0) { return NextResponse.json({ error: "ids required" }, { status: 400 }); } if (!(await canAccessTransactions(user.id, ids.map(Number)))) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } if (action === "categorize" && category) { const ops = ids.map((id) => prisma.transaction_overrides.upsert({ where: { transaction_id: id }, update: { category_override: category, updated_at: new Date() }, create: { transaction_id: id, category_override: category }, }) ); await prisma.$transaction(ops); return NextResponse.json({ updated: ids.length }); } if (action === "normalize" && merchant_normalized) { const ops = ids.map((id) => prisma.transaction_overrides.upsert({ where: { transaction_id: id }, update: { merchant_normalized, updated_at: new Date() }, create: { transaction_id: id, merchant_normalized }, }) ); await prisma.$transaction(ops); return NextResponse.json({ updated: ids.length }); } if (action === "split" && Array.isArray(splits) && splits.length > 0) { const total = splits.reduce((s, x) => s + x.share_percent, 0); if (Math.abs(total - 100) > 0.01) { return NextResponse.json({ error: "Shares must sum to 100%" }, { status: 400 }); } await prisma.$transaction( ids.flatMap((id) => [ prisma.transaction_splits.deleteMany({ where: { transaction_id: id } }), prisma.transaction_splits.createMany({ data: splits.map((s) => ({ transaction_id: id, participant_id: s.participant_id, share_percent: s.share_percent, })), }), ]) ); return NextResponse.json({ updated: ids.length }); } if ((action === "tag" || action === "untag") && tag_id) { if (action === "tag") { for (const id of ids) { await queryRaw( `INSERT INTO transaction_tags (transaction_id, tag_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`, [id, tag_id] ); } } else { await queryRaw( `DELETE FROM transaction_tags WHERE transaction_id = ANY($1::int[]) AND tag_id = $2`, [ids, tag_id] ); } return NextResponse.json({ updated: ids.length }); } if (action === "assign_trip") { const { trip_id } = body as { ids: number[]; trip_id: number | null }; await assignTransactionsToTrip(trip_id, ids); return NextResponse.json({ updated: ids.length }); } return NextResponse.json({ error: "Invalid action" }, { status: 400 }); }