feat(cash): mark how a transaction was paid, exclude cash from reconciliation
ci / lint-test (push) Successful in 41s
ci / lint-test (push) Successful in 41s
getPendingReconciliations treated every unreconciled manual transaction as awaiting a matching statement row. Cash never appears on a statement, so a cash entry sat in the queue indefinitely being offered matches within 3 days and 1% on amount - and accepting one is silently destructive: reconciled manual rows are filtered out of every query, so the cash spend disappears while the card transaction it matched claims to be that same spend. Migration 0016 adds transactions.payment_method (card | cash | bank_transfer | other, NULL = unknown) with a CHECK constraint and a partial index. The notCash() fragment excludes cash from both halves of the reconciliation query - the pending list and the candidate match subquery, which aliases the manual row as m. Only cash is excluded. Bank transfers do appear on a statement now that transaction accounts are imported, and NULL means unknown, so both stay candidates and every pre-existing row behaves exactly as before. ATM withdrawals deliberately stay categorised as spend rather than transfers. Treating them as transfers is only correct if every cash purchase is logged; with partial logging it silently deletes the unlogged remainder from spend.
This commit is contained in:
@@ -34,11 +34,12 @@ export async function PATCH(
|
||||
}
|
||||
const body = await req.json();
|
||||
|
||||
const { category, merchant_normalized, notes, transaction_type, my_share_percent, description, amount, transaction_date, trip_id } = body as {
|
||||
const { category, merchant_normalized, notes, transaction_type, my_share_percent, description, amount, transaction_date, trip_id, payment_method } = body as {
|
||||
category?: string;
|
||||
merchant_normalized?: string;
|
||||
notes?: string;
|
||||
transaction_type?: string;
|
||||
payment_method?: string | null;
|
||||
my_share_percent?: number | null;
|
||||
description?: string;
|
||||
amount?: number;
|
||||
@@ -84,6 +85,19 @@ export async function PATCH(
|
||||
);
|
||||
}
|
||||
|
||||
// payment_method is a property of the transaction itself, not a user override
|
||||
// of extracted data, so it lives on the transactions table.
|
||||
if (payment_method !== undefined) {
|
||||
const VALID_METHODS = ["card", "cash", "bank_transfer", "other"];
|
||||
if (payment_method !== null && !VALID_METHODS.includes(payment_method)) {
|
||||
return NextResponse.json({ error: "Invalid payment_method" }, { status: 400 });
|
||||
}
|
||||
await queryRaw(
|
||||
`UPDATE transactions SET payment_method = $1 WHERE id = $2`,
|
||||
[payment_method, transactionId]
|
||||
);
|
||||
}
|
||||
|
||||
// category/merchant/notes/my_share_percent/trip_id go through the overrides table
|
||||
const hasOverride = category !== undefined || merchant_normalized !== undefined || notes !== undefined || my_share_percent !== undefined || trip_id !== undefined;
|
||||
if (!hasOverride) {
|
||||
|
||||
Reference in New Issue
Block a user