- New split_payments table records actual payments between participants - Balance = total split obligations - total payments (splits never marked settled) - Record Payment modal per participant: direction toggle, amount pre-filled with balance, date, notes - Payment history inline on each balance card with +/- display and delete - Per-transaction Settle button removed; Action column removed from shared table - Splits always show the true cost breakdown regardless of payment state
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { getCurrentUser } from "@/lib/auth";
|
|
import { queryRaw } from "@/lib/db";
|
|
import { prisma } from "@/lib/db";
|
|
|
|
export async function GET(req: NextRequest) {
|
|
const user = await getCurrentUser(req);
|
|
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 403 });
|
|
|
|
const sp = req.nextUrl.searchParams;
|
|
const participantId = sp.get("participant_id");
|
|
|
|
// Return payment history between current user and a participant
|
|
const rows = await queryRaw<{
|
|
id: number;
|
|
from_participant_id: number;
|
|
from_name: string;
|
|
to_participant_id: number;
|
|
to_name: string;
|
|
amount: number;
|
|
payment_date: string;
|
|
notes: string | null;
|
|
linked_transaction_id: number | null;
|
|
created_at: string;
|
|
}>(
|
|
`SELECT sp.id, sp.from_participant_id, pf.name as from_name,
|
|
sp.to_participant_id, pt.name as to_name,
|
|
sp.amount, sp.payment_date, sp.notes,
|
|
sp.linked_transaction_id, sp.created_at
|
|
FROM split_payments sp
|
|
JOIN participants pf ON pf.id = sp.from_participant_id
|
|
JOIN participants pt ON pt.id = sp.to_participant_id
|
|
WHERE (sp.from_participant_id = $1 OR sp.to_participant_id = $1)
|
|
AND (sp.from_participant_id = $2 OR sp.to_participant_id = $2)
|
|
ORDER BY sp.payment_date DESC, sp.created_at DESC`,
|
|
[user.id, participantId ? Number(participantId) : user.id]
|
|
);
|
|
|
|
return NextResponse.json(rows);
|
|
}
|
|
|
|
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() as {
|
|
from_participant_id: number;
|
|
to_participant_id: number;
|
|
amount: number;
|
|
payment_date: string;
|
|
notes?: string;
|
|
linked_transaction_id?: number;
|
|
};
|
|
|
|
const { from_participant_id, to_participant_id, amount, payment_date, notes, linked_transaction_id } = body;
|
|
|
|
if (!from_participant_id || !to_participant_id || !amount || !payment_date) {
|
|
return NextResponse.json({ error: "Missing required fields" }, { status: 400 });
|
|
}
|
|
if (amount <= 0) {
|
|
return NextResponse.json({ error: "Amount must be positive" }, { status: 400 });
|
|
}
|
|
|
|
const payment = await prisma.split_payments.create({
|
|
data: {
|
|
from_participant_id,
|
|
to_participant_id,
|
|
amount,
|
|
payment_date: new Date(payment_date),
|
|
notes: notes || null,
|
|
linked_transaction_id: linked_transaction_id || null,
|
|
},
|
|
});
|
|
|
|
return NextResponse.json(payment);
|
|
}
|
|
|
|
export async function DELETE(req: NextRequest) {
|
|
const user = await getCurrentUser(req);
|
|
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 403 });
|
|
|
|
const sp = req.nextUrl.searchParams;
|
|
const id = Number(sp.get("id"));
|
|
if (!id) return NextResponse.json({ error: "id required" }, { status: 400 });
|
|
|
|
await prisma.split_payments.delete({ where: { id } });
|
|
return NextResponse.json({ ok: true });
|
|
}
|