Removes voided/flagged entry flags

Updates journal entry flags to align with Beancount's limited flag support.
Beancount only uses cleared (*) and pending (!) flags.

Removes the VOID and FLAGGED flags and recommends using tags instead
(e.g., "! + #voided" for voided entries, "! + #review" for flagged entries).

Updates the API to reflect this change, removing the ability to directly
"reject" an expense entry via the void flag.  Instead, instructs users to
add the #voided tag in Fava.

Updates reconciliation summary to count entries with voided/review tags
instead of voided/flagged flags.
This commit is contained in:
padreug 2025-11-09 23:26:30 +01:00
parent de3e4e65af
commit 9350f05d74
2 changed files with 21 additions and 17 deletions

View file

@ -15,11 +15,18 @@ class AccountType(str, Enum):
class JournalEntryFlag(str, Enum):
"""Transaction status flags (Beancount-style)"""
"""Transaction status flags (Beancount-compatible)
Beancount only supports two user-facing flags:
- * (CLEARED): Completed transactions
- ! (PENDING): Transactions needing attention
For voided/flagged transactions, use tags instead:
- Voided: Use "!" flag + #voided tag
- Flagged: Use "!" flag + #review tag
"""
CLEARED = "*" # Fully reconciled/confirmed
PENDING = "!" # Not yet confirmed/awaiting approval
FLAGGED = "#" # Needs review/attention
VOID = "x" # Voided/cancelled entry
class Account(BaseModel):

View file

@ -1924,19 +1924,14 @@ async def api_reject_expense_entry(
detail=f"Entry is not pending (current status: {entry.flag.value})",
)
# Update flag to voided
await db.execute(
"""
UPDATE journal_entries
SET flag = :flag
WHERE id = :id
""",
{"flag": JournalEntryFlag.VOID.value, "id": entry_id}
# Since entries are now in Fava/Beancount, voiding requires editing the Beancount file
# Beancount doesn't have a "void" flag - recommend using ! flag + #voided tag
raise HTTPException(
status_code=HTTPStatus.NOT_IMPLEMENTED,
detail="To reject/void entry, open Fava and either delete the transaction or add the #voided tag. "
"Beancount only supports * (cleared) and ! (pending) flags."
)
# Return updated entry
return await get_journal_entry(entry_id)
# ===== BALANCE ASSERTION ENDPOINTS =====
@ -2136,11 +2131,13 @@ async def api_get_reconciliation_summary(
fava = get_fava_client()
all_entries = await fava.query_transactions(limit=1000, include_pending=True)
# Count entries by flag
# Count entries by flag (Beancount only supports * and !)
cleared = len([e for e in all_entries if e.get("flag") == "*"])
pending_entries = len([e for e in all_entries if e.get("flag") == "!"])
flagged = len([e for e in all_entries if e.get("flag") == "#"])
voided = len([e for e in all_entries if e.get("flag") == "x"])
# Count entries with special tags
voided = len([e for e in all_entries if "voided" in e.get("tags", [])])
flagged = len([e for e in all_entries if "review" in e.get("tags", []) or "flagged" in e.get("tags", [])])
# Get all accounts
accounts = await get_all_accounts()