""" Economy/Ledger Management Module Handles accounts, transactions, bank accounts, and financial reporting """ from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List, Optional from datetime import datetime, date from decimal import Decimal from app.database import get_db from app.models import Account, Transaction, BankAccount, AccountType, User from app.core.security import get_current_user router = APIRouter(prefix="/economy", tags=["economy"]) # ─── ACCOUNTS ───────────────────────────────────────────────────────────────── @router.get("/accounts") async def list_accounts( account_type: Optional[AccountType] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """List all accounts with optional filtering.""" query = db.query(Account) if account_type: query = query.filter(Account.account_type == account_type) accounts = query.all() return { "accounts": [ { "id": a.id, "account_number": a.account_number, "name": a.name, "type": a.account_type.value, "balance": float(a.balance) if a.balance else 0, "currency": a.currency, "is_active": a.is_active, } for a in accounts ], "total": query.count(), } @router.post("/accounts") async def create_account( account_number: str, name: str, account_type: AccountType, parent_id: Optional[int] = None, description: Optional[str] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Create a new account.""" account = Account( account_number=account_number, name=name, account_type=account_type, parent_id=parent_id, description=description, ) db.add(account) db.commit() db.refresh(account) return {"status": "created", "account_id": account.id} @router.get("/accounts/{account_id}") async def get_account( account_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Get account details.""" account = db.query(Account).filter(Account.id == account_id).first() if not account: raise HTTPException(status_code=404, detail="Account not found") return { "id": account.id, "account_number": account.account_number, "name": account.name, "type": account.account_type.value, "balance": float(account.balance) if account.balance else 0, "currency": account.currency, "description": account.description, "is_active": account.is_active, } # ─── TRANSACTIONS ───────────────────────────────────────────────────────────── @router.get("/transactions") async def list_transactions( account_id: Optional[int] = None, start_date: Optional[date] = None, end_date: Optional[date] = None, skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """List transactions with filtering.""" query = db.query(Transaction) if account_id: query = query.filter( (Transaction.debit_account_id == account_id) | (Transaction.credit_account_id == account_id) ) if start_date: query = query.filter(Transaction.date >= start_date) if end_date: query = query.filter(Transaction.date <= end_date) transactions = query.order_by(Transaction.date.desc()).offset(skip).limit(limit).all() return { "transactions": [ { "id": t.id, "transaction_id": t.transaction_id, "date": t.date.isoformat() if t.date else None, "description": t.description, "amount": float(t.amount) if t.amount else 0, "debit_account_id": t.debit_account_id, "credit_account_id": t.credit_account_id, "status": t.status, "reference": t.reference, } for t in transactions ], "total": query.count(), } @router.post("/transactions") async def create_transaction( transaction_id: str, date: datetime, description: str, amount: float, debit_account_id: int, credit_account_id: int, reference: Optional[str] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Create a new transaction (double-entry bookkeeping).""" # Verify accounts exist debit_account = db.query(Account).filter(Account.id == debit_account_id).first() credit_account = db.query(Account).filter(Account.id == credit_account_id).first() if not debit_account or not credit_account: raise HTTPException(status_code=404, detail="One or both accounts not found") transaction = Transaction( transaction_id=transaction_id, date=date, description=description, amount=Decimal(str(amount)), debit_account_id=debit_account_id, credit_account_id=credit_account_id, reference=reference, created_by=current_user.id, ) # Update account balances debit_account.balance += Decimal(str(amount)) credit_account.balance -= Decimal(str(amount)) db.add(transaction) db.commit() db.refresh(transaction) return {"status": "created", "transaction_id": transaction.id} # ─── BANK ACCOUNTS ──────────────────────────────────────────────────────────── @router.get("/bank-accounts") async def list_bank_accounts( db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """List all bank accounts.""" accounts = db.query(BankAccount).all() return { "accounts": [ { "id": a.id, "bank_name": a.bank_name, "account_name": a.account_name, "account_number": a.account_number, "iban": a.iban, "bic": a.bic, "currency": a.currency, "current_balance": float(a.current_balance) if a.current_balance else 0, "account_type": a.account_type, "is_active": a.is_active, } for a in accounts ] } @router.post("/bank-accounts") async def create_bank_account( bank_name: str, account_name: str, account_number: Optional[str] = None, iban: Optional[str] = None, bic: Optional[str] = None, currency: str = "SEK", account_type: Optional[str] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Create a new bank account.""" account = BankAccount( bank_name=bank_name, account_name=account_name, account_number=account_number, iban=iban, bic=bic, currency=currency, account_type=account_type, ) db.add(account) db.commit() db.refresh(account) return {"status": "created", "bank_account_id": account.id} # ─── REPORTS ────────────────────────────────────────────────────────────────── @router.get("/reports/balance-sheet") async def get_balance_sheet( as_of: Optional[date] = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Generate balance sheet report.""" assets = db.query(Account).filter(Account.account_type == AccountType.ASSET).all() liabilities = db.query(Account).filter(Account.account_type == AccountType.LIABILITY).all() equity = db.query(Account).filter(Account.account_type == AccountType.EQUITY).all() return { "report_type": "Balance Sheet", "as_of": as_of.isoformat() if as_of else datetime.utcnow().date().isoformat(), "assets": { "total": sum(float(a.balance) for a in assets), "accounts": [{"name": a.name, "balance": float(a.balance)} for a in assets] }, "liabilities": { "total": sum(float(l.balance) for l in liabilities), "accounts": [{"name": l.name, "balance": float(l.balance)} for l in liabilities] }, "equity": { "total": sum(float(e.balance) for e in equity), "accounts": [{"name": e.name, "balance": float(e.balance)} for e in equity] }, } @router.get("/reports/income-statement") async def get_income_statement( start_date: date, end_date: date, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Generate income statement (resultaträkning).""" revenues = db.query(Account).filter(Account.account_type == AccountType.REVENUE).all() expenses = db.query(Account).filter(Account.account_type == AccountType.EXPENSE).all() total_revenue = sum(float(r.balance) for r in revenues) total_expenses = sum(float(e.balance) for e in expenses) return { "report_type": "Income Statement", "period": {"start": start_date.isoformat(), "end": end_date.isoformat()}, "revenue": { "total": total_revenue, "accounts": [{"name": r.name, "balance": float(r.balance)} for r in revenues] }, "expenses": { "total": total_expenses, "accounts": [{"name": e.name, "balance": float(e.balance)} for e in expenses] }, "net_income": total_revenue - total_expenses, }