From e6f5d5a33b26dfdea851610740d0c64115c681e0 Mon Sep 17 00:00:00 2001 From: jerick Date: Tue, 21 Apr 2026 09:47:49 -0400 Subject: [PATCH] reverted budget allocation for credit cards --- src/app/api/admin/fix-cc-types/route.ts | 35 +++++++++++++++++++++++++ src/app/api/upload/route.ts | 16 +++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/app/api/admin/fix-cc-types/route.ts diff --git a/src/app/api/admin/fix-cc-types/route.ts b/src/app/api/admin/fix-cc-types/route.ts new file mode 100644 index 0000000..b2698e9 --- /dev/null +++ b/src/app/api/admin/fix-cc-types/route.ts @@ -0,0 +1,35 @@ +import { NextResponse } from 'next/server' +import { auth } from '@/lib/auth' +import { prisma } from '@/lib/prisma' + +// One-time migration: flips DEBIT↔CREDIT on all CREDIT_CARD account transactions. +// CC CSVs have inverted sign semantics (CREDIT = charge, DEBIT = refund), but we now +// normalise them to standard semantics (DEBIT = spend) at upload time. Existing rows +// uploaded before that fix need their type flipped. +export async function POST() { + const session = await auth() + if (!session?.user?.id) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + + const userId = session.user.id + + const ccAccounts = await prisma.account.findMany({ + where: { userId, type: 'CREDIT_CARD' }, + select: { id: true }, + }) + + if (ccAccounts.length === 0) { + return NextResponse.json({ updated: 0, message: 'No credit card accounts found' }) + } + + const ccIds = ccAccounts.map((a) => a.id) + + // Flip both directions in one raw statement to avoid stepping on our own update + const result = await prisma.$executeRaw` + UPDATE "Transaction" + SET type = CASE WHEN type = 'DEBIT' THEN 'CREDIT'::"TransactionType" + ELSE 'DEBIT'::"TransactionType" END + WHERE "accountId" = ANY(${ccIds}::text[]) + ` + + return NextResponse.json({ updated: result }) +} diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts index 4908594..24992ae 100644 --- a/src/app/api/upload/route.ts +++ b/src/app/api/upload/route.ts @@ -124,6 +124,22 @@ export async function POST(req: Request) { })), skipDuplicates: true, }) + + // For CC accounts, also correct the type on any rows that already existed + // (uploaded before the CREDIT↔DEBIT flip was introduced). + if (account.type === 'CREDIT_CARD') { + const debitHashes = rowsWithBudgets.filter((r) => r.type === 'DEBIT').map((r) => r.dedupeHash) + const creditHashes = rowsWithBudgets.filter((r) => r.type === 'CREDIT').map((r) => r.dedupeHash) + await Promise.all([ + debitHashes.length > 0 + ? prisma.transaction.updateMany({ where: { dedupeHash: { in: debitHashes }, type: 'CREDIT' }, data: { type: 'DEBIT' } }) + : Promise.resolve(), + creditHashes.length > 0 + ? prisma.transaction.updateMany({ where: { dedupeHash: { in: creditHashes }, type: 'DEBIT' }, data: { type: 'CREDIT' } }) + : Promise.resolve(), + ]) + } + const skippedCount = normalized.length - importedCount // Recompute current balance