Fix audit findings: budget editing, dead code, logging, multi-currency

- Add budget editing: updateBudget() API, edit button on budget cards,
  BudgetFormModal adapted for create/update (category locked on edit)
- Remove permanently-broken POST /auth/totp/verify stub and its unused
  TOTPVerifyRequest schema
- Wire getHoldingTransactions() to AssetDetail page — transaction history
  table now shows above the candlestick chart, sorted newest-first
- Fix multi-currency net worth in account_service: account balances are
  now converted to base_currency via ExchangeRate table before summing
- Replace silent bare pass exception handlers with logger.warning() in
  transactions.py (OCR/AI pipeline) and price_feed_service.py (search)
  — ValueError in date/number regex parsing left silent (control flow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-04-23 10:54:32 +00:00
parent 312594f3d2
commit 8ef3bb2965
9 changed files with 181 additions and 64 deletions

View file

@ -1,6 +1,6 @@
import { useParams, Link } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { getPriceHistory, getPortfolio } from "@/api/investments";
import { getPriceHistory, getPortfolio, getHoldingTransactions } from "@/api/investments";
import { formatCurrency } from "@/utils/currency";
import { useUiStore } from "@/store/uiStore";
import { cn } from "@/utils/cn";
@ -20,6 +20,12 @@ export default function AssetDetail() {
enabled: !!assetId,
});
const { data: transactions = [] } = useQuery({
queryKey: ["holding-transactions", holding?.id],
queryFn: () => getHoldingTransactions(holding!.id),
enabled: !!holding?.id,
});
const dates = prices.map(p => p.date);
const opens = prices.map(p => p.open ?? p.close);
const highs = prices.map(p => p.high ?? p.close);
@ -73,6 +79,62 @@ export default function AssetDetail() {
</div>
)}
{/* Transaction history */}
{holding && (
<div className="bg-card border border-border rounded-xl overflow-hidden">
<div className="px-4 py-3 border-b border-border">
<p className="text-sm font-medium">Transaction History</p>
</div>
{transactions.length === 0 ? (
<p className="text-sm text-muted-foreground text-center py-8">No transactions recorded</p>
) : (
<table className="w-full text-sm">
<thead>
<tr className="text-xs text-muted-foreground uppercase tracking-wider border-b border-border">
<th className="text-left px-4 py-2">Date</th>
<th className="text-left px-4 py-2">Type</th>
<th className="text-right px-4 py-2">Quantity</th>
<th className="text-right px-4 py-2">Price</th>
<th className="text-right px-4 py-2 hidden sm:table-cell">Fees</th>
<th className="text-right px-4 py-2">Total</th>
</tr>
</thead>
<tbody>
{[...transactions].sort((a, b) => b.date.localeCompare(a.date)).map((t) => {
const isBuy = t.type === "buy" || t.type === "transfer_in";
const isSell = t.type === "sell" || t.type === "transfer_out";
return (
<tr key={t.id} className="border-b border-border/50 last:border-0 hover:bg-secondary/20 transition-colors">
<td className="px-4 py-2.5 tabular-nums text-muted-foreground">{t.date}</td>
<td className="px-4 py-2.5">
<span className={cn(
"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium",
isBuy ? "bg-success/15 text-success" :
isSell ? "bg-destructive/15 text-destructive" :
"bg-secondary text-muted-foreground"
)}>
{t.type.replace("_", " ")}
</span>
</td>
<td className="px-4 py-2.5 text-right tabular-nums">{Number(t.quantity).toLocaleString()}</td>
<td className="px-4 py-2.5 text-right tabular-nums">{formatCurrency(t.price, t.currency)}</td>
<td className="px-4 py-2.5 text-right tabular-nums hidden sm:table-cell text-muted-foreground">
{t.fees > 0 ? formatCurrency(t.fees, t.currency) : "—"}
</td>
<td className={cn("px-4 py-2.5 text-right tabular-nums font-medium",
isBuy ? "text-destructive" : isSell ? "text-success" : ""
)}>
{isSell ? "+" : isBuy ? "-" : ""}{formatCurrency(t.total_amount, t.currency)}
</td>
</tr>
);
})}
</tbody>
</table>
)}
</div>
)}
{/* Candlestick chart */}
<div className="bg-card border border-border rounded-xl p-4">
<p className="text-sm font-medium mb-3">Price History (1 Year)</p>