Files
blackroad-os-pack-finance/models/ledger_entry.py
Alexa Louise 0d1d083485
Some checks failed
Auto Deploy PR / detect-and-deploy (push) Has been cancelled
Deploy to Railway / build (push) Has been cancelled
Deploy to Railway / deploy (push) Has been cancelled
Stale Issue Cleanup / stale (push) Failing after 1m13s
Pi deployment mega-session: 136+ containers deployed
Massive deployment session deploying entire BlackRoad/Lucidia infrastructure to Raspberry Pi 4B:
- Cleaned /tmp space: 595MB → 5.2GB free
- Total containers: 136+ running simultaneously
- Ports: 3067-3200+
- Disk: 25G/29G (92% usage)
- Memory: 3.6Gi/7.9Gi

Deployment scripts created:
- /tmp/continue-deploy.sh (v2-* deployments)
- /tmp/absolute-final-deploy.sh (final-* deployments)
- /tmp/deployment-status.sh (monitoring)

Infrastructure maximized on single Pi 4B (8GB RAM, 32GB SD).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 23:10:31 -06:00

102 lines
2.8 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
from typing import Literal
@dataclass
class LedgerEntry:
"""Represents a financial ledger entry."""
id: str
timestamp: datetime
account: str
description: str
amount: Decimal
currency: str = "USD"
entry_type: Literal["debit", "credit"] = "debit"
category: str | None = None
tags: list[str] | None = None
metadata: dict[str, str] | None = None
def __post_init__(self):
"""Ensure amount is a Decimal."""
if not isinstance(self.amount, Decimal):
self.amount = Decimal(str(self.amount))
if self.tags is None:
self.tags = []
if self.metadata is None:
self.metadata = {}
def to_dict(self) -> dict:
"""Convert to dictionary representation."""
return {
"id": self.id,
"timestamp": self.timestamp.isoformat(),
"account": self.account,
"description": self.description,
"amount": str(self.amount),
"currency": self.currency,
"entry_type": self.entry_type,
"category": self.category,
"tags": self.tags,
"metadata": self.metadata,
}
from datetime import date
from typing import Optional
from pydantic import BaseModel, Field, validator
class LedgerEntry(BaseModel):
date: date = Field(..., description="Transaction date")
account: str = Field(..., description="Account name")
debit: float | None = Field(0.0, description="Debit amount")
credit: float | None = Field(0.0, description="Credit amount")
description: Optional[str] = Field(None, description="Entry detail")
@validator("debit", "credit", pre=True)
def empty_to_zero(cls, value: float | None) -> float:
return float(value or 0)
@validator("debit", "credit")
def non_negative(cls, value: float) -> float:
if value < 0:
raise ValueError("Amounts must be non-negative")
return value
@property
def net(self) -> float:
return (self.debit or 0) - (self.credit or 0)
def is_balanced(self) -> bool:
return abs(self.net) < 1e-6
class LedgerFile(BaseModel):
name: str
entries: list[LedgerEntry]
def total_debits(self) -> float:
return sum(e.debit or 0 for e in self.entries)
def total_credits(self) -> float:
return sum(e.credit or 0 for e in self.entries)
def imbalance(self) -> float:
return round(self.total_debits() - self.total_credits(), 2)
def summary(self) -> dict[str, float]:
return {
"entries": len(self.entries),
"debits": self.total_debits(),
"credits": self.total_credits(),
"imbalance": self.imbalance(),
}