lomavuokraus/app/api/billing-settings/route.ts
Tero Halla-aho 49a1096152
Some checks are pending
CI / checks (push) Waiting to run
Add billing agent opt-in settings and per-listing overrides
2025-12-13 22:23:47 +02:00

140 lines
5.1 KiB
TypeScript

import { NextResponse } from 'next/server';
import { prisma } from '../../../lib/prisma';
import { requireAuth } from '../../../lib/jwt';
import { UserStatus } from '@prisma/client';
const selectListingLabel = (translations: { title: string; locale: string; slug: string }[]) => {
if (!translations || translations.length === 0) return 'Listing';
const sorted = [...translations].sort((a, b) => a.locale.localeCompare(b.locale));
return sorted[0].title || sorted[0].slug || 'Listing';
};
export async function GET(req: Request) {
try {
const session = await requireAuth(req);
const user = await prisma.user.findUnique({
where: { id: session.userId },
select: {
status: true,
agentBillingEnabled: true,
agentBankAccount: true,
agentVatBreakdownRequired: true,
agentUseListingOverrides: true,
},
});
if (!user || user.status !== UserStatus.ACTIVE) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const listings = await prisma.listing.findMany({
where: { ownerId: session.userId },
select: {
id: true,
translations: { select: { title: true, slug: true, locale: true } },
billingSettings: { select: { bankAccount: true, vatBreakdownRequired: true } },
},
orderBy: { createdAt: 'desc' },
});
const listingOverrides = listings.map((listing) => ({
id: listing.id,
label: selectListingLabel(listing.translations),
bankAccount: listing.billingSettings?.bankAccount ?? '',
vatBreakdownRequired: listing.billingSettings?.vatBreakdownRequired ?? false,
}));
return NextResponse.json({
settings: {
agentBillingEnabled: user.agentBillingEnabled,
agentBankAccount: user.agentBankAccount ?? '',
agentVatBreakdownRequired: user.agentVatBreakdownRequired,
agentUseListingOverrides: user.agentUseListingOverrides,
},
listingOverrides,
});
} catch (error) {
console.error('Billing settings fetch error', error);
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}
export async function PATCH(req: Request) {
try {
const session = await requireAuth(req);
const body = await req.json();
const user = await prisma.user.findUnique({ where: { id: session.userId }, select: { status: true } });
if (!user || user.status !== UserStatus.ACTIVE) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const agentBillingEnabled = Boolean(body.agentBillingEnabled);
const agentVatBreakdownRequired = Boolean(body.agentVatBreakdownRequired);
const agentUseListingOverrides = Boolean(body.agentUseListingOverrides);
const agentBankAccountRaw = typeof body.agentBankAccount === 'string' ? body.agentBankAccount.trim() : '';
const agentBankAccount = agentBankAccountRaw.length ? agentBankAccountRaw : null;
const updates = await prisma.$transaction(async (tx) => {
const updatedUser = await tx.user.update({
where: { id: session.userId },
data: {
agentBillingEnabled,
agentVatBreakdownRequired,
agentUseListingOverrides,
agentBankAccount,
},
select: {
agentBillingEnabled: true,
agentBankAccount: true,
agentVatBreakdownRequired: true,
agentUseListingOverrides: true,
},
});
const listingOverrides: any[] = Array.isArray(body.listingOverrides) ? body.listingOverrides : [];
const sanitizedOverrides = listingOverrides
.map((o) => ({
listingId: typeof o.listingId === 'string' ? o.listingId : '',
bankAccount: typeof o.bankAccount === 'string' ? o.bankAccount.trim() : '',
vatBreakdownRequired: Boolean(o.vatBreakdownRequired),
}))
.filter((o) => o.listingId);
for (const override of sanitizedOverrides) {
const listing = await tx.listing.findFirst({ where: { id: override.listingId, ownerId: session.userId }, select: { id: true } });
if (!listing) continue;
await tx.listingBillingSettings.upsert({
where: { listingId: override.listingId },
update: {
bankAccount: override.bankAccount || null,
vatBreakdownRequired: override.vatBreakdownRequired,
},
create: {
listingId: override.listingId,
bankAccount: override.bankAccount || null,
vatBreakdownRequired: override.vatBreakdownRequired,
},
});
}
return updatedUser;
});
return NextResponse.json({
ok: true,
settings: {
agentBillingEnabled: updates.agentBillingEnabled,
agentBankAccount: updates.agentBankAccount ?? '',
agentVatBreakdownRequired: updates.agentVatBreakdownRequired,
agentUseListingOverrides: updates.agentUseListingOverrides,
},
});
} catch (error) {
console.error('Billing settings update error', error);
return NextResponse.json({ error: 'Failed to update billing settings' }, { status: 500 });
}
}
export const dynamic = 'force-dynamic';