diff --git a/CLAUDE.md b/CLAUDE.md index 6376629..a80a185 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -229,8 +229,8 @@ await create_account(CreateAccount( await create_journal_entry(CreateJournalEntry( description="Cash payment for groceries", lines=[ - CreateEntryLine(account_id=expense_account_id, debit=50000), - CreateEntryLine(account_id=cash_account_id, credit=50000) + CreateEntryLine(account_id=expense_account_id, amount=50000), # Positive = debit (expense increase) + CreateEntryLine(account_id=cash_account_id, amount=-50000) # Negative = credit (asset decrease) ], flag=JournalEntryFlag.CLEARED, meta={"source": "manual", "payment_method": "cash"} diff --git a/docs/BEANCOUNT_PATTERNS.md b/docs/BEANCOUNT_PATTERNS.md index 907ebc6..2124c92 100644 --- a/docs/BEANCOUNT_PATTERNS.md +++ b/docs/BEANCOUNT_PATTERNS.md @@ -61,8 +61,7 @@ class ImmutableEntryLine(NamedTuple): id: str journal_entry_id: str account_id: str - debit: int - credit: int + amount: int # Beancount-style: positive = debit, negative = credit description: Optional[str] metadata: dict[str, Any] flag: Optional[str] # Like Beancount: '!', '*', etc. @@ -145,15 +144,14 @@ class CastlePlugin(Protocol): __plugins__ = ('check_all_balanced',) def check_all_balanced(entries, settings, config): - """Verify all journal entries have debits = credits""" + """Verify all journal entries balance (sum of amounts = 0)""" errors = [] for entry in entries: - total_debits = sum(line.debit for line in entry.lines) - total_credits = sum(line.credit for line in entry.lines) - if total_debits != total_credits: + total_amount = sum(line.amount for line in entry.lines) + if total_amount != 0: errors.append({ 'entry_id': entry.id, - 'message': f'Unbalanced entry: debits={total_debits}, credits={total_credits}', + 'message': f'Unbalanced entry: sum of amounts={total_amount} (must equal 0)', 'severity': 'error' }) return entries, errors @@ -184,7 +182,7 @@ def check_receivable_limits(entries, settings, config): for line in entry.lines: if 'Accounts Receivable' in line.account_name: user_id = extract_user_from_account(line.account_name) - receivables[user_id] = receivables.get(user_id, 0) + line.debit - line.credit + receivables[user_id] = receivables.get(user_id, 0) + line.amount for user_id, amount in receivables.items(): if amount > max_per_user: @@ -367,22 +365,15 @@ async def get_user_inventory(user_id: str) -> CastleInventory: # Add as position metadata = json.loads(line.metadata) if line.metadata else {} - if line.debit > 0: + if line.amount != 0: + # Beancount-style: positive = debit, negative = credit + # Adjust sign for cost amount based on amount direction + cost_sign = 1 if line.amount > 0 else -1 inventory.add_position(CastlePosition( currency="SATS", - amount=Decimal(line.debit), + amount=Decimal(line.amount), cost_currency=metadata.get("fiat_currency"), - cost_amount=Decimal(metadata.get("fiat_amount", 0)), - date=line.created_at, - metadata=metadata - )) - - if line.credit > 0: - inventory.add_position(CastlePosition( - currency="SATS", - amount=-Decimal(line.credit), - cost_currency=metadata.get("fiat_currency"), - cost_amount=-Decimal(metadata.get("fiat_amount", 0)), + cost_amount=cost_sign * Decimal(metadata.get("fiat_amount", 0)), date=line.created_at, metadata=metadata )) @@ -840,17 +831,16 @@ class UnbalancedEntryError(NamedTuple): async def validate_journal_entry(entry: CreateJournalEntry) -> list[CastleError]: errors = [] - total_debits = sum(line.debit for line in entry.lines) - total_credits = sum(line.credit for line in entry.lines) + # Beancount-style: sum of amounts must equal 0 + total_amount = sum(line.amount for line in entry.lines) - if total_debits != total_credits: + if total_amount != 0: errors.append(UnbalancedEntryError( source={'created_by': entry.created_by}, - message=f"Entry does not balance: debits={total_debits}, credits={total_credits}", + message=f"Entry does not balance: sum of amounts={total_amount} (must equal 0)", entry=entry.dict(), - total_debits=total_debits, - total_credits=total_credits, - difference=total_debits - total_credits + total_amount=total_amount, + difference=total_amount )) return errors diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md index 936802b..ac79f03 100644 --- a/docs/DOCUMENTATION.md +++ b/docs/DOCUMENTATION.md @@ -71,8 +71,7 @@ CREATE TABLE entry_lines ( id TEXT PRIMARY KEY, journal_entry_id TEXT NOT NULL, account_id TEXT NOT NULL, - debit INTEGER NOT NULL DEFAULT 0, -- Amount in satoshis - credit INTEGER NOT NULL DEFAULT 0, -- Amount in satoshis + amount INTEGER NOT NULL, -- Amount in satoshis (positive = debit, negative = credit) description TEXT, metadata TEXT DEFAULT '{}' -- JSON: {fiat_currency, fiat_amount, fiat_rate, btc_rate} ); @@ -314,17 +313,20 @@ for account in user_accounts: total_balance -= account_balance # Positive asset = User owes Castle, so negative balance # Calculate fiat balance from metadata + # Beancount-style: positive amount = debit, negative amount = credit for line in account_entry_lines: if line.metadata.fiat_currency and line.metadata.fiat_amount: if account.account_type == AccountType.LIABILITY: - if line.credit > 0: + # For liabilities, negative amounts (credits) increase what castle owes + if line.amount < 0: fiat_balances[currency] += fiat_amount # Castle owes more - elif line.debit > 0: + else: fiat_balances[currency] -= fiat_amount # Castle owes less elif account.account_type == AccountType.ASSET: - if line.debit > 0: + # For assets, positive amounts (debits) increase what user owes + if line.amount > 0: fiat_balances[currency] -= fiat_amount # User owes more (negative balance) - elif line.credit > 0: + else: fiat_balances[currency] += fiat_amount # User owes less ``` @@ -767,10 +769,8 @@ async def export_beancount( beancount_name = format_account_name(account.name, account.user_id) beancount_type = map_account_type(account.account_type) - if line.debit > 0: - amount = line.debit - else: - amount = -line.credit + # Beancount-style: amount is already signed (positive = debit, negative = credit) + amount = line.amount lines.append(f" {beancount_type}:{beancount_name} {amount} SATS") diff --git a/docs/EXPENSE_APPROVAL.md b/docs/EXPENSE_APPROVAL.md index b8b3261..3123b32 100644 --- a/docs/EXPENSE_APPROVAL.md +++ b/docs/EXPENSE_APPROVAL.md @@ -41,7 +41,7 @@ Only entries with `flag='*'` (CLEARED) are included in balance calculations: ```sql -- Balance query excludes pending/flagged/voided entries -SELECT SUM(debit), SUM(credit) +SELECT SUM(amount) FROM entry_lines el JOIN journal_entries je ON el.journal_entry_id = je.id WHERE el.account_id = :account_id diff --git a/docs/PHASE3_COMPLETE.md b/docs/PHASE3_COMPLETE.md index bce9a76..1a3dbb6 100644 --- a/docs/PHASE3_COMPLETE.md +++ b/docs/PHASE3_COMPLETE.md @@ -276,8 +276,8 @@ balance = BalanceCalculator.calculate_account_balance( # Build inventory from entry lines entry_lines = [ - {"debit": 100000, "credit": 0, "metadata": '{"fiat_currency": "EUR", "fiat_amount": "50.00"}'}, - {"debit": 0, "credit": 50000, "metadata": "{}"} + {"amount": 100000, "metadata": '{"fiat_currency": "EUR", "fiat_amount": "50.00"}'}, # Positive = debit + {"amount": -50000, "metadata": "{}"} # Negative = credit ] inventory = BalanceCalculator.build_inventory_from_entry_lines( @@ -306,8 +306,8 @@ entry = { } entry_lines = [ - {"account_id": "acc1", "debit": 100000, "credit": 0}, - {"account_id": "acc2", "debit": 0, "credit": 100000} + {"account_id": "acc1", "amount": 100000}, # Positive = debit + {"account_id": "acc2", "amount": -100000} # Negative = credit ] try: