Completes Phase 1: Beancount patterns adoption
Implements core improvements from Phase 1 of the Beancount patterns adoption: - Uses Decimal for fiat amounts to prevent floating point errors - Adds a meta field to journal entries for a full audit trail - Adds a flag field to journal entries for transaction status - Migrates existing account names to a hierarchical format This commit introduces a database migration to add the `flag` and `meta` columns to the `journal_entries` table. It also includes updates to the models, CRUD operations, and API endpoints to handle the new fields.
This commit is contained in:
parent
35d2057694
commit
1a28ec59eb
7 changed files with 616 additions and 31 deletions
21
models.py
21
models.py
|
|
@ -1,4 +1,5 @@
|
|||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -13,6 +14,14 @@ class AccountType(str, Enum):
|
|||
EXPENSE = "expense"
|
||||
|
||||
|
||||
class JournalEntryFlag(str, Enum):
|
||||
"""Transaction status flags (Beancount-style)"""
|
||||
CLEARED = "*" # Fully reconciled/confirmed
|
||||
PENDING = "!" # Not yet confirmed/awaiting approval
|
||||
FLAGGED = "#" # Needs review/attention
|
||||
VOID = "x" # Voided/cancelled entry
|
||||
|
||||
|
||||
class Account(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
|
|
@ -55,6 +64,8 @@ class JournalEntry(BaseModel):
|
|||
created_at: datetime
|
||||
reference: Optional[str] = None # Invoice ID or reference number
|
||||
lines: list[EntryLine] = []
|
||||
flag: JournalEntryFlag = JournalEntryFlag.CLEARED # Transaction status
|
||||
meta: dict = {} # Metadata: source, tags, links, notes, etc.
|
||||
|
||||
|
||||
class CreateJournalEntry(BaseModel):
|
||||
|
|
@ -62,20 +73,22 @@ class CreateJournalEntry(BaseModel):
|
|||
entry_date: Optional[datetime] = None
|
||||
reference: Optional[str] = None
|
||||
lines: list[CreateEntryLine]
|
||||
flag: JournalEntryFlag = JournalEntryFlag.CLEARED
|
||||
meta: dict = {}
|
||||
|
||||
|
||||
class UserBalance(BaseModel):
|
||||
user_id: str
|
||||
balance: int # positive = castle owes user, negative = user owes castle
|
||||
accounts: list[Account] = []
|
||||
fiat_balances: dict[str, float] = {} # e.g. {"EUR": 250.0, "USD": 100.0}
|
||||
fiat_balances: dict[str, Decimal] = {} # e.g. {"EUR": Decimal("250.0"), "USD": Decimal("100.0")}
|
||||
|
||||
|
||||
class ExpenseEntry(BaseModel):
|
||||
"""Helper model for creating expense entries"""
|
||||
|
||||
description: str
|
||||
amount: float # Amount in the specified currency (or satoshis if currency is None)
|
||||
amount: Decimal # Amount in the specified currency (or satoshis if currency is None)
|
||||
expense_account: str # account name or ID
|
||||
is_equity: bool = False # True = equity contribution, False = liability (castle owes user)
|
||||
user_wallet: str
|
||||
|
|
@ -87,7 +100,7 @@ class ReceivableEntry(BaseModel):
|
|||
"""Helper model for creating accounts receivable entries"""
|
||||
|
||||
description: str
|
||||
amount: float # Amount in the specified currency (or satoshis if currency is None)
|
||||
amount: Decimal # Amount in the specified currency (or satoshis if currency is None)
|
||||
revenue_account: str # account name or ID
|
||||
user_id: str # The user_id (not wallet_id) of the user who owes the castle
|
||||
reference: Optional[str] = None
|
||||
|
|
@ -98,7 +111,7 @@ class RevenueEntry(BaseModel):
|
|||
"""Helper model for creating revenue entries"""
|
||||
|
||||
description: str
|
||||
amount: float # Amount in the specified currency (or satoshis if currency is None)
|
||||
amount: Decimal # Amount in the specified currency (or satoshis if currency is None)
|
||||
revenue_account: str
|
||||
payment_method_account: str # e.g., "Cash", "Bank", "Lightning"
|
||||
reference: Optional[str] = None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue