After deleting transactions, recalculate currentBalanceCents for each affected account so the account card and net worth dashboard stay accurate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
2.7 KiB
TypeScript
87 lines
2.7 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { z } from 'zod'
|
|
import { auth } from '@/lib/auth'
|
|
import { prisma } from '@/lib/prisma'
|
|
|
|
const bulkSchema = z.discriminatedUnion('action', [
|
|
z.object({
|
|
action: z.literal('delete'),
|
|
ids: z.array(z.string()).min(1),
|
|
}),
|
|
z.object({
|
|
action: z.literal('assignBudget'),
|
|
ids: z.array(z.string()).min(1),
|
|
budgetId: z.string().nullable(),
|
|
}),
|
|
z.object({
|
|
action: z.literal('addNotes'),
|
|
ids: z.array(z.string()).min(1),
|
|
notes: z.string().max(500),
|
|
}),
|
|
])
|
|
|
|
export async function POST(req: Request) {
|
|
const session = await auth()
|
|
if (!session?.user?.id) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
|
|
const body = await req.json()
|
|
const result = bulkSchema.safeParse(body)
|
|
if (!result.success) return NextResponse.json({ error: result.error.flatten() }, { status: 400 })
|
|
|
|
const { action, ids } = result.data
|
|
const userId = session.user.id
|
|
|
|
// Verify all transaction IDs belong to this user
|
|
const owned = await prisma.transaction.findMany({
|
|
where: { id: { in: ids }, account: { userId } },
|
|
select: { id: true, accountId: true },
|
|
})
|
|
if (owned.length !== ids.length) {
|
|
return NextResponse.json({ error: 'One or more transactions not found' }, { status: 404 })
|
|
}
|
|
|
|
if (action === 'delete') {
|
|
const accountIds = [...new Set(owned.map((t) => t.accountId))]
|
|
|
|
await prisma.transaction.deleteMany({ where: { id: { in: ids } } })
|
|
|
|
// Recompute currentBalanceCents for each affected account
|
|
for (const accountId of accountIds) {
|
|
const [balRow] = await prisma.$queryRaw<[{ balance: bigint }]>`
|
|
SELECT COALESCE(SUM(
|
|
CASE WHEN type = 'CREDIT' THEN "amountCents" ELSE -"amountCents" END
|
|
), 0)::bigint AS balance
|
|
FROM "Transaction"
|
|
WHERE "accountId" = ${accountId}
|
|
`
|
|
await prisma.account.update({
|
|
where: { id: accountId },
|
|
data: { currentBalanceCents: Number(balRow.balance) },
|
|
})
|
|
}
|
|
|
|
return NextResponse.json({ deleted: ids.length })
|
|
}
|
|
|
|
if (action === 'assignBudget') {
|
|
const { budgetId } = result.data
|
|
if (budgetId !== null) {
|
|
const budget = await prisma.budget.findFirst({ where: { id: budgetId, userId } })
|
|
if (!budget) return NextResponse.json({ error: 'Budget not found' }, { status: 404 })
|
|
}
|
|
await prisma.transaction.updateMany({
|
|
where: { id: { in: ids } },
|
|
data: { budgetId },
|
|
})
|
|
return NextResponse.json({ updated: ids.length })
|
|
}
|
|
|
|
// addNotes
|
|
const { notes } = result.data
|
|
await prisma.transaction.updateMany({
|
|
where: { id: { in: ids } },
|
|
data: { notes: notes || null },
|
|
})
|
|
return NextResponse.json({ updated: ids.length })
|
|
}
|