diff --git a/src/app/api/analytics/merchants/[merchant]/route.ts b/src/app/api/analytics/merchants/[merchant]/route.ts index 28bb4d5..2361bd3 100644 --- a/src/app/api/analytics/merchants/[merchant]/route.ts +++ b/src/app/api/analytics/merchants/[merchant]/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import { getCurrentUser } from "@/lib/auth"; import { queryRaw } from "@/lib/db"; -import { OWNER_SCOPE, STATEMENTS_JOIN } from "@/lib/analytics-sql"; +import { OWNER_SCOPE, STATEMENTS_JOIN, EFFECTIVE_CATEGORY } from "@/lib/analytics-sql"; export async function GET( req: NextRequest, @@ -38,7 +38,7 @@ export async function GET( (CASE WHEN ts.share_percent IS NOT NULL THEN COALESCE(t.amount_aud, t.amount) * ts.share_percent / 100 WHEN o.my_share_percent IS NOT NULL THEN COALESCE(t.amount_aud, t.amount) * o.my_share_percent / 100 ELSE COALESCE(t.amount_aud, t.amount) END) END::numeric(10,2) as my_amount, t.transaction_type, - COALESCE(o.category_override, t.category) as category, + ${EFFECTIVE_CATEGORY} as category, COALESCE(s.bank_name, 'Manual') as bank_name, t.statement_id FROM transactions t diff --git a/src/app/api/analytics/merchants/route.ts b/src/app/api/analytics/merchants/route.ts index 45799c5..23cdc41 100644 --- a/src/app/api/analytics/merchants/route.ts +++ b/src/app/api/analytics/merchants/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from "next/server"; import { getCurrentUser } from "@/lib/auth"; import { queryRaw } from "@/lib/db"; -import { OWNER_SCOPE, STATEMENTS_JOIN, EXCLUDE_NON_SPEND } from "@/lib/analytics-sql"; +import { OWNER_SCOPE, STATEMENTS_JOIN, EXCLUDE_NON_SPEND, EFFECTIVE_CATEGORY } from "@/lib/analytics-sql"; // Split-adjusted amount helper (positive for spend, negative for refunds) const MY_AMOUNT = `CASE WHEN ts.share_percent IS NOT NULL THEN COALESCE(t.amount_aud, t.amount) * ts.share_percent / 100 WHEN o.my_share_percent IS NOT NULL THEN COALESCE(t.amount_aud, t.amount) * o.my_share_percent / 100 ELSE COALESCE(t.amount_aud, t.amount) END`; @@ -39,7 +39,7 @@ export async function GET(req: NextRequest) { }>(` SELECT COALESCE(o.merchant_normalized, t.merchant_normalized, t.merchant_name, t.description) as merchant, - MODE() WITHIN GROUP (ORDER BY COALESCE(o.category_override, t.category)) as category, + MODE() WITHIN GROUP (ORDER BY ${EFFECTIVE_CATEGORY}) as category, COUNT(*) FILTER (WHERE t.transaction_type IN ('debit', 'fee', 'interest'))::int as debit_count, COUNT(*) FILTER (WHERE t.transaction_type IN ('refund', 'credit'))::int as refund_count, COALESCE(SUM( diff --git a/src/lib/categories.ts b/src/lib/categories.ts index 1178815..8a74989 100644 --- a/src/lib/categories.ts +++ b/src/lib/categories.ts @@ -36,7 +36,10 @@ export const CATEGORIES = [ export type Category = (typeof CATEGORIES)[number]; -export function formatCategory(cat: string): string { +// Tolerates null/undefined: an uncategorised transaction must never be able to +// crash a page that renders its category. +export function formatCategory(cat: string | null | undefined): string { + if (!cat) return "Uncategorised"; return cat .split("_") .map((w) => w.charAt(0).toUpperCase() + w.slice(1))