Refactors user entry retrieval from Fava

Switches to retrieving all journal entries from Fava and filtering in the application to allow filtering by account type and user.
This provides more flexibility and control over the data being presented to the user.
Also extracts and includes relevant metadata such as entry ID, fiat amounts, and references for improved frontend display.
This commit is contained in:
padreug 2025-11-10 00:47:52 +01:00
parent 7f545ea88e
commit 63d851ce94

View file

@ -315,38 +315,131 @@ async def api_get_user_entries(
# Regular user can only see their own entries
target_user_id = wallet.wallet.user
# Query Fava for transactions
if target_user_id:
# Build account pattern based on account_type filter
if filter_account_type:
# Filter by account type (asset = receivable, liability = payable)
if filter_account_type.lower() == "asset":
account_pattern = f"Receivable:User-{target_user_id[:8]}"
elif filter_account_type.lower() == "liability":
account_pattern = f"Payable:User-{target_user_id[:8]}"
else:
account_pattern = f"User-{target_user_id[:8]}"
# Get all journal entries from Fava (full transaction objects)
all_entries = await fava.get_journal_entries()
# Filter and transform entries
filtered_entries = []
for e in all_entries:
if e.get("t") != "Transaction":
continue
# Skip voided transactions
if "voided" in e.get("tags", []):
continue
# Extract user ID from metadata or account names
user_id_match = None
entry_meta = e.get("meta", {})
if "user-id" in entry_meta:
user_id_match = entry_meta["user-id"]
else:
# All user accounts
account_pattern = f"User-{target_user_id[:8]}"
# Try to extract from account names in postings
for posting in e.get("postings", []):
account = posting.get("account", "")
if "User-" in account:
# Extract user ID from account name (e.g., "Liabilities:Payable:User-abc123")
parts = account.split("User-")
if len(parts) > 1:
user_id_match = parts[1] # Just the short ID after User-
break
entries = await fava.query_transactions(
account_pattern=account_pattern,
limit=limit + offset # Fava doesn't support offset, so fetch more and slice
)
# Apply offset
entries = entries[offset:offset + limit]
total = len(entries) # Note: This is approximate since we don't know the true total
else:
# Super user viewing all entries
entries = await fava.query_transactions(limit=limit + offset)
entries = entries[offset:offset + limit]
total = len(entries)
# Filter by target user if specified
if target_user_id and user_id_match:
if not user_id_match.startswith(target_user_id[:8]):
continue
# Filter by account type if specified
if filter_account_type and user_id_match:
postings = e.get("postings", [])
has_matching_account = False
for posting in postings:
account = posting.get("account", "")
if filter_account_type.lower() == "asset" and "Receivable" in account:
has_matching_account = True
break
elif filter_account_type.lower() == "liability" and "Payable" in account:
has_matching_account = True
break
if not has_matching_account:
continue
# Extract data for frontend
# Extract entry ID from links
entry_id = None
links = e.get("links", [])
if isinstance(links, (list, set)):
for link in links:
if isinstance(link, str):
link_clean = link.lstrip('^')
if "castle-" in link_clean:
parts = link_clean.split("castle-")
if len(parts) > 1:
entry_id = parts[-1]
break
# Extract amount from postings
amount_sats = 0
fiat_amount = None
fiat_currency = None
postings = e.get("postings", [])
if postings:
first_posting = postings[0]
if isinstance(first_posting, dict):
amount_field = first_posting.get("amount")
if isinstance(amount_field, dict):
amount_sats = abs(int(float(amount_field.get("number", 0))))
cost = first_posting.get("cost")
if isinstance(cost, dict):
fiat_amount = float(cost.get("number", 0))
fiat_currency = cost.get("currency")
# Extract reference from links (first non-castle link)
reference = None
if isinstance(links, (list, set)):
for link in links:
if isinstance(link, str):
link_clean = link.lstrip('^')
if not link_clean.startswith("castle-") and not link_clean.startswith("ln-"):
reference = link_clean
break
# Get username from user ID (first 8 chars for display)
username = f"User-{user_id_match[:8]}" if user_id_match else None
entry_data = {
"id": entry_id or e.get("entry_hash", "unknown"),
"date": e.get("date", ""),
"entry_date": e.get("date", ""),
"flag": e.get("flag"),
"description": e.get("narration", ""),
"payee": e.get("payee"),
"tags": e.get("tags", []),
"links": links,
"amount": amount_sats,
"user_id": user_id_match,
"username": username,
"reference": reference,
"meta": entry_meta, # Include metadata for frontend
}
if fiat_amount and fiat_currency:
entry_data["fiat_amount"] = fiat_amount
entry_data["fiat_currency"] = fiat_currency
filtered_entries.append(entry_data)
# Sort by date descending
filtered_entries.sort(key=lambda x: x.get("date", ""), reverse=True)
# Apply pagination
total = len(filtered_entries)
paginated_entries = filtered_entries[offset:offset + limit]
# Fava transactions already contain the data we need
# Metadata includes user-id, account information, etc.
return {
"entries": entries,
"entries": paginated_entries,
"total": total,
"limit": limit,
"offset": offset,