feat: CRUD-API Konten/Buchungen/Kategorien/Regeln
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
0
finance/app/services/__init__.py
Normal file
0
finance/app/services/__init__.py
Normal file
26
finance/app/services/balances.py
Normal file
26
finance/app/services/balances.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.tables import Account, Statement, Transaction
|
||||
|
||||
|
||||
def account_balance(session: Session, account: Account) -> Decimal:
|
||||
stmt = session.execute(
|
||||
select(Statement).where(Statement.account_id == account.id,
|
||||
Statement.status == "confirmed")
|
||||
.order_by(Statement.period_end.desc())).scalars().first()
|
||||
q = select(func.coalesce(func.sum(Transaction.amount), 0)).where(
|
||||
Transaction.account_id == account.id, Transaction.status == "confirmed")
|
||||
if stmt is not None:
|
||||
q = q.where(Transaction.booking_date > stmt.period_end)
|
||||
base = stmt.closing_balance
|
||||
else:
|
||||
base = Decimal("0")
|
||||
return Decimal(base) + Decimal(session.execute(q).scalar_one())
|
||||
|
||||
|
||||
def total_balance(session: Session) -> Decimal:
|
||||
accounts = session.execute(select(Account)).scalars().all()
|
||||
return sum((account_balance(session, a) for a in accounts), Decimal("0"))
|
||||
20
finance/app/services/categorize.py
Normal file
20
finance/app/services/categorize.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.tables import CategoryRule, Transaction
|
||||
|
||||
|
||||
def apply_rules(session: Session, transactions: list[Transaction]) -> int:
|
||||
rules = session.execute(
|
||||
select(CategoryRule).order_by(CategoryRule.priority)).scalars().all()
|
||||
hits = 0
|
||||
for tx in transactions:
|
||||
if tx.category_id is not None:
|
||||
continue
|
||||
haystack = f"{tx.purpose} {tx.counterparty}".lower()
|
||||
for rule in rules:
|
||||
if rule.pattern.lower() in haystack:
|
||||
tx.category_id = rule.category_id
|
||||
hits += 1
|
||||
break
|
||||
return hits
|
||||
Reference in New Issue
Block a user