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-[80px]">Ref #</TableHead>
<TableHead>Description</TableHead>
<TableHead>Payee / Payor</TableHead>
<TableHead>Category</TableHead>
<TableHead className="text-right w-[110px]">Payment</TableHead>
<TableHead className="text-right w-[110px]">Deposit</TableHead>
@@ -845,7 +846,7 @@ export default function AccountingBankingPage() {
<TableCell className="px-2" />
<TableCell className="text-sm text-muted-foreground">{fmtDate(periodFrom)}</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 />
</TableRow>
@@ -873,6 +874,7 @@ export default function AccountingBankingPage() {
<span className={row.voided ? "line-through text-muted-foreground" : ""}>{row.description}</span>
</span>
</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-right text-sm text-red-600 ${row.voided ? "line-through opacity-60" : ""}`}>
{row.type === "debit" ? money(row.amount, cur) : ""}
@@ -905,14 +907,14 @@ export default function AccountingBankingPage() {
))}
{filteredRegister.length === 0 && (
<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.
</TableCell>
</TableRow>
)}
{filteredRegister.length > 0 && (
<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-emerald-600">{money(totalCredits, cur)}</TableCell>
<TableCell className={`text-right text-sm ${computedBalance < 0 ? "text-destructive" : ""}`}>
@@ -77,7 +77,7 @@ export default function AccountingDepositsPage() {
queryFn: async () => {
const { data } = await accounting
.from("transactions")
.select("*")
.select("*, vendors(name), customers(name)")
.eq("company_id", cid)
.eq("account_id", undepositedId)
.eq("type", "debit")
@@ -93,7 +93,7 @@ export default function AccountingDepositsPage() {
queryFn: async () => {
const { data } = await accounting
.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("deposited", 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 rows: PendingRow[] = [
...(pendingTx as any[]).map((t) => ({
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) => ({
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",
payor: p.customers?.name ?? null,
reference: p.reference ?? null, amount: Number(p.amount),
})),
];
@@ -329,6 +331,7 @@ export default function AccountingDepositsPage() {
</TableHead>
<TableHead>Date</TableHead>
<TableHead>Description</TableHead>
<TableHead>Payee / Payor</TableHead>
<TableHead>Reference</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
@@ -343,13 +346,14 @@ export default function AccountingDepositsPage() {
<TableCell><input type="checkbox" checked={selected.has(r.key)} readOnly /></TableCell>
<TableCell>{fmtDate(r.date)}</TableCell>
<TableCell>{r.description}</TableCell>
<TableCell className="text-sm">{r.payor ?? "—"}</TableCell>
<TableCell className="text-muted-foreground">{r.reference ?? "—"}</TableCell>
<TableCell className="text-right tabular-nums">{money(r.amount, cur)}</TableCell>
</TableRow>
))}
{pending.length === 0 && (
<TableRow className="hover:bg-transparent">
<TableCell colSpan={5} className="p-0">
<TableCell colSpan={6} className="p-0">
<EmptyState
icon={Landmark}
title="No payments awaiting deposit"
@@ -53,6 +53,9 @@ type Tx = {
cleared: boolean;
reconciliation_id: string | null;
voided?: boolean;
vendors?: { name: string } | null;
customers?: { name: string } | null;
payee_name?: string | null;
};
export default function AccountingReconcileDetailPage() {
@@ -147,7 +150,7 @@ export default function AccountingReconcileDetailPage() {
// and not voided.
const { data } = await accounting
.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)
.is("reconciliation_id", null)
.eq("voided", false)
@@ -469,6 +472,7 @@ export default function AccountingReconcileDetailPage() {
</TableHead>
<SortHead col="date" label="Date" 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="deposit" label="Deposit" 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>{fmtDate(t.date)}</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-right text-emerald-700">
{t.type === "credit" ? money(t.amount, cur) : ""}
@@ -514,7 +519,7 @@ export default function AccountingReconcileDetailPage() {
);
})}
{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.
</TableCell></TableRow>
)}