Accounting: show Payee/Payor on transaction-style lists

Add a Payee / Payor column to the bank register (Banking), the reconciliation
list, and the Deposits 'awaiting deposit' list — showing the vendor (payee) for
money out or the customer/homeowner (payor) for money in. Expenses already shows
Vendor and Receive Payments shows Homeowner, so those were left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 17:49:26 -04:00
parent 1370b98be9
commit 5714543533
3 changed files with 21 additions and 10 deletions
@@ -832,6 +832,7 @@ export default function AccountingBankingPage() {
<TableHead className="w-[100px]">Date</TableHead> <TableHead className="w-[100px]">Date</TableHead>
<TableHead className="w-[80px]">Ref #</TableHead> <TableHead className="w-[80px]">Ref #</TableHead>
<TableHead>Description</TableHead> <TableHead>Description</TableHead>
<TableHead>Payee / Payor</TableHead>
<TableHead>Category</TableHead> <TableHead>Category</TableHead>
<TableHead className="text-right w-[110px]">Payment</TableHead> <TableHead className="text-right w-[110px]">Payment</TableHead>
<TableHead className="text-right w-[110px]">Deposit</TableHead> <TableHead className="text-right w-[110px]">Deposit</TableHead>
@@ -845,7 +846,7 @@ export default function AccountingBankingPage() {
<TableCell className="px-2" /> <TableCell className="px-2" />
<TableCell className="text-sm text-muted-foreground">{fmtDate(periodFrom)}</TableCell> <TableCell className="text-sm text-muted-foreground">{fmtDate(periodFrom)}</TableCell>
<TableCell /> <TableCell />
<TableCell className="text-sm font-medium text-muted-foreground" colSpan={4}>Balance forward</TableCell> <TableCell className="text-sm font-medium text-muted-foreground" colSpan={5}>Balance forward</TableCell>
<TableCell className="text-right text-sm font-medium tabular-nums">{money(balanceForward, cur)}</TableCell> <TableCell className="text-right text-sm font-medium tabular-nums">{money(balanceForward, cur)}</TableCell>
<TableCell /> <TableCell />
</TableRow> </TableRow>
@@ -873,6 +874,7 @@ export default function AccountingBankingPage() {
<span className={row.voided ? "line-through text-muted-foreground" : ""}>{row.description}</span> <span className={row.voided ? "line-through text-muted-foreground" : ""}>{row.description}</span>
</span> </span>
</TableCell> </TableCell>
<TableCell className="text-sm">{row.vendors?.name ?? row.customers?.name ?? row.payee_name ?? "—"}</TableCell>
<TableCell className="text-sm text-muted-foreground">{row.category ?? "—"}</TableCell> <TableCell className="text-sm text-muted-foreground">{row.category ?? "—"}</TableCell>
<TableCell className={`text-right text-sm text-red-600 ${row.voided ? "line-through opacity-60" : ""}`}> <TableCell className={`text-right text-sm text-red-600 ${row.voided ? "line-through opacity-60" : ""}`}>
{row.type === "debit" ? money(row.amount, cur) : ""} {row.type === "debit" ? money(row.amount, cur) : ""}
@@ -905,14 +907,14 @@ export default function AccountingBankingPage() {
))} ))}
{filteredRegister.length === 0 && ( {filteredRegister.length === 0 && (
<TableRow> <TableRow>
<TableCell colSpan={9} className="text-center text-muted-foreground py-8"> <TableCell colSpan={10} className="text-center text-muted-foreground py-8">
No transactions yet. Record a deposit or payment to get started. No transactions yet. Record a deposit or payment to get started.
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
{filteredRegister.length > 0 && ( {filteredRegister.length > 0 && (
<TableRow className="bg-muted font-medium"> <TableRow className="bg-muted font-medium">
<TableCell colSpan={5} className="text-sm">Totals</TableCell> <TableCell colSpan={6} className="text-sm">Totals</TableCell>
<TableCell className="text-right text-sm text-red-600">{money(totalDebits, cur)}</TableCell> <TableCell className="text-right text-sm text-red-600">{money(totalDebits, cur)}</TableCell>
<TableCell className="text-right text-sm text-emerald-600">{money(totalCredits, cur)}</TableCell> <TableCell className="text-right text-sm text-emerald-600">{money(totalCredits, cur)}</TableCell>
<TableCell className={`text-right text-sm ${computedBalance < 0 ? "text-destructive" : ""}`}> <TableCell className={`text-right text-sm ${computedBalance < 0 ? "text-destructive" : ""}`}>
@@ -77,7 +77,7 @@ export default function AccountingDepositsPage() {
queryFn: async () => { queryFn: async () => {
const { data } = await accounting const { data } = await accounting
.from("transactions") .from("transactions")
.select("*") .select("*, vendors(name), customers(name)")
.eq("company_id", cid) .eq("company_id", cid)
.eq("account_id", undepositedId) .eq("account_id", undepositedId)
.eq("type", "debit") .eq("type", "debit")
@@ -93,7 +93,7 @@ export default function AccountingDepositsPage() {
queryFn: async () => { queryFn: async () => {
const { data } = await accounting const { data } = await accounting
.from("payments_received") .from("payments_received")
.select("id,payment_date,amount,method,reference,memo,customer_id") .select("id,payment_date,amount,method,reference,memo,customer_id,customers(name)")
.eq("company_id", cid) .eq("company_id", cid)
.eq("deposited", false) .eq("deposited", false)
.order("payment_date", { ascending: false }); .order("payment_date", { ascending: false });
@@ -101,17 +101,19 @@ export default function AccountingDepositsPage() {
}, },
}); });
type PendingRow = { key: string; kind: "tx" | "pmt"; id: string; date: string; description: string; reference: string | null; amount: number }; type PendingRow = { key: string; kind: "tx" | "pmt"; id: string; date: string; description: string; payor: string | null; reference: string | null; amount: number };
const pending = useMemo<PendingRow[]>(() => { const pending = useMemo<PendingRow[]>(() => {
const rows: PendingRow[] = [ const rows: PendingRow[] = [
...(pendingTx as any[]).map((t) => ({ ...(pendingTx as any[]).map((t) => ({
key: `tx:${t.id}`, kind: "tx" as const, id: t.id, date: t.date, key: `tx:${t.id}`, kind: "tx" as const, id: t.id, date: t.date,
description: t.description, reference: t.reference ?? null, amount: Number(t.amount), description: t.description, payor: t.customers?.name ?? t.vendors?.name ?? t.payee_name ?? null,
reference: t.reference ?? null, amount: Number(t.amount),
})), })),
...(pendingPmt as any[]).map((p) => ({ ...(pendingPmt as any[]).map((p) => ({
key: `pmt:${p.id}`, kind: "pmt" as const, id: p.id, date: p.payment_date, key: `pmt:${p.id}`, kind: "pmt" as const, id: p.id, date: p.payment_date,
description: [p.method, p.memo].filter(Boolean).join(" · ") || "Customer payment", description: [p.method, p.memo].filter(Boolean).join(" · ") || "Customer payment",
payor: p.customers?.name ?? null,
reference: p.reference ?? null, amount: Number(p.amount), reference: p.reference ?? null, amount: Number(p.amount),
})), })),
]; ];
@@ -329,6 +331,7 @@ export default function AccountingDepositsPage() {
</TableHead> </TableHead>
<TableHead>Date</TableHead> <TableHead>Date</TableHead>
<TableHead>Description</TableHead> <TableHead>Description</TableHead>
<TableHead>Payee / Payor</TableHead>
<TableHead>Reference</TableHead> <TableHead>Reference</TableHead>
<TableHead className="text-right">Amount</TableHead> <TableHead className="text-right">Amount</TableHead>
</TableRow> </TableRow>
@@ -343,13 +346,14 @@ export default function AccountingDepositsPage() {
<TableCell><input type="checkbox" checked={selected.has(r.key)} readOnly /></TableCell> <TableCell><input type="checkbox" checked={selected.has(r.key)} readOnly /></TableCell>
<TableCell>{fmtDate(r.date)}</TableCell> <TableCell>{fmtDate(r.date)}</TableCell>
<TableCell>{r.description}</TableCell> <TableCell>{r.description}</TableCell>
<TableCell className="text-sm">{r.payor ?? "—"}</TableCell>
<TableCell className="text-muted-foreground">{r.reference ?? "—"}</TableCell> <TableCell className="text-muted-foreground">{r.reference ?? "—"}</TableCell>
<TableCell className="text-right tabular-nums">{money(r.amount, cur)}</TableCell> <TableCell className="text-right tabular-nums">{money(r.amount, cur)}</TableCell>
</TableRow> </TableRow>
))} ))}
{pending.length === 0 && ( {pending.length === 0 && (
<TableRow className="hover:bg-transparent"> <TableRow className="hover:bg-transparent">
<TableCell colSpan={5} className="p-0"> <TableCell colSpan={6} className="p-0">
<EmptyState <EmptyState
icon={Landmark} icon={Landmark}
title="No payments awaiting deposit" title="No payments awaiting deposit"
@@ -53,6 +53,9 @@ type Tx = {
cleared: boolean; cleared: boolean;
reconciliation_id: string | null; reconciliation_id: string | null;
voided?: boolean; voided?: boolean;
vendors?: { name: string } | null;
customers?: { name: string } | null;
payee_name?: string | null;
}; };
export default function AccountingReconcileDetailPage() { export default function AccountingReconcileDetailPage() {
@@ -147,7 +150,7 @@ export default function AccountingReconcileDetailPage() {
// and not voided. // and not voided.
const { data } = await accounting const { data } = await accounting
.from("transactions") .from("transactions")
.select("id,date,description,reference,amount,type,cleared,reconciliation_id,voided") .select("id,date,description,reference,amount,type,cleared,reconciliation_id,voided,payee_name,vendors(name),customers(name)")
.eq("account_id", accountId) .eq("account_id", accountId)
.is("reconciliation_id", null) .is("reconciliation_id", null)
.eq("voided", false) .eq("voided", false)
@@ -469,6 +472,7 @@ export default function AccountingReconcileDetailPage() {
</TableHead> </TableHead>
<SortHead col="date" label="Date" sort={sort} onSort={toggleSort} /> <SortHead col="date" label="Date" sort={sort} onSort={toggleSort} />
<SortHead col="description" label="Description" sort={sort} onSort={toggleSort} /> <SortHead col="description" label="Description" sort={sort} onSort={toggleSort} />
<TableHead>Payee / Payor</TableHead>
<SortHead col="reference" label="Ref #" sort={sort} onSort={toggleSort} /> <SortHead col="reference" label="Ref #" sort={sort} onSort={toggleSort} />
<SortHead col="deposit" label="Deposit" sort={sort} onSort={toggleSort} align="right" /> <SortHead col="deposit" label="Deposit" sort={sort} onSort={toggleSort} align="right" />
<SortHead col="withdrawal" label="Withdrawal" sort={sort} onSort={toggleSort} align="right" /> <SortHead col="withdrawal" label="Withdrawal" sort={sort} onSort={toggleSort} align="right" />
@@ -497,6 +501,7 @@ export default function AccountingReconcileDetailPage() {
</TableCell> </TableCell>
<TableCell>{fmtDate(t.date)}</TableCell> <TableCell>{fmtDate(t.date)}</TableCell>
<TableCell className="max-w-[280px] truncate">{t.description}</TableCell> <TableCell className="max-w-[280px] truncate">{t.description}</TableCell>
<TableCell className="text-sm">{t.vendors?.name ?? t.customers?.name ?? t.payee_name ?? "—"}</TableCell>
<TableCell className="text-muted-foreground">{t.reference ?? "—"}</TableCell> <TableCell className="text-muted-foreground">{t.reference ?? "—"}</TableCell>
<TableCell className="text-right text-emerald-700"> <TableCell className="text-right text-emerald-700">
{t.type === "credit" ? money(t.amount, cur) : ""} {t.type === "credit" ? money(t.amount, cur) : ""}
@@ -514,7 +519,7 @@ export default function AccountingReconcileDetailPage() {
); );
})} })}
{filtered.length === 0 && ( {filtered.length === 0 && (
<TableRow><TableCell colSpan={7} className="text-center text-muted-foreground py-8"> <TableRow><TableCell colSpan={8} className="text-center text-muted-foreground py-8">
No unreconciled transactions for this account. No unreconciled transactions for this account.
</TableCell></TableRow> </TableCell></TableRow>
)} )}