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 # Regular user can only see their own entries
target_user_id = wallet.wallet.user target_user_id = wallet.wallet.user
# Query Fava for transactions # Get all journal entries from Fava (full transaction objects)
if target_user_id: all_entries = await fava.get_journal_entries()
# Build account pattern based on account_type filter
if filter_account_type: # Filter and transform entries
# Filter by account type (asset = receivable, liability = payable) filtered_entries = []
if filter_account_type.lower() == "asset": for e in all_entries:
account_pattern = f"Receivable:User-{target_user_id[:8]}" if e.get("t") != "Transaction":
elif filter_account_type.lower() == "liability": continue
account_pattern = f"Payable:User-{target_user_id[:8]}"
else: # Skip voided transactions
account_pattern = f"User-{target_user_id[:8]}" 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: else:
# All user accounts # Try to extract from account names in postings
account_pattern = f"User-{target_user_id[:8]}" 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( # Filter by target user if specified
account_pattern=account_pattern, if target_user_id and user_id_match:
limit=limit + offset # Fava doesn't support offset, so fetch more and slice if not user_id_match.startswith(target_user_id[:8]):
) continue
# Apply offset
entries = entries[offset:offset + limit] # Filter by account type if specified
total = len(entries) # Note: This is approximate since we don't know the true total if filter_account_type and user_id_match:
else: postings = e.get("postings", [])
# Super user viewing all entries has_matching_account = False
entries = await fava.query_transactions(limit=limit + offset) for posting in postings:
entries = entries[offset:offset + limit] account = posting.get("account", "")
total = len(entries) 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 { return {
"entries": entries, "entries": paginated_entries,
"total": total, "total": total,
"limit": limit, "limit": limit,
"offset": offset, "offset": offset,