95 lines
3.5 KiB
Python
95 lines
3.5 KiB
Python
"""Transaction service"""
|
|
|
|
from typing import Optional, List
|
|
from datetime import datetime, timedelta
|
|
from sqlalchemy.orm import Session
|
|
from app.db.repositories import TransactionRepository, AccountRepository, BudgetRepository
|
|
from app.db.models import Transaction, TransactionType
|
|
from app.schemas import TransactionCreateSchema
|
|
|
|
|
|
class TransactionService:
|
|
"""Service for transaction operations"""
|
|
|
|
def __init__(self, session: Session):
|
|
self.session = session
|
|
self.transaction_repo = TransactionRepository(session)
|
|
self.account_repo = AccountRepository(session)
|
|
self.budget_repo = BudgetRepository(session)
|
|
|
|
def create_transaction(
|
|
self,
|
|
family_id: int,
|
|
user_id: int,
|
|
account_id: int,
|
|
data: TransactionCreateSchema,
|
|
) -> Transaction:
|
|
"""Create new transaction and update account balance"""
|
|
# Create transaction
|
|
transaction = self.transaction_repo.create(
|
|
family_id=family_id,
|
|
user_id=user_id,
|
|
account_id=account_id,
|
|
amount=data.amount,
|
|
transaction_type=data.transaction_type,
|
|
description=data.description,
|
|
notes=data.notes,
|
|
tags=data.tags,
|
|
category_id=data.category_id,
|
|
receipt_photo_url=data.receipt_photo_url,
|
|
transaction_date=data.transaction_date,
|
|
)
|
|
|
|
# Update account balance
|
|
if data.transaction_type == TransactionType.EXPENSE:
|
|
self.account_repo.update_balance(account_id, -data.amount)
|
|
elif data.transaction_type == TransactionType.INCOME:
|
|
self.account_repo.update_balance(account_id, data.amount)
|
|
|
|
# Update budget if expense
|
|
if (
|
|
data.transaction_type == TransactionType.EXPENSE
|
|
and data.category_id
|
|
):
|
|
budget = self.budget_repo.get_category_budget(family_id, data.category_id)
|
|
if budget:
|
|
self.budget_repo.update_spent_amount(budget.id, data.amount)
|
|
|
|
return transaction
|
|
|
|
def get_family_summary(self, family_id: int, days: int = 30) -> dict:
|
|
"""Get financial summary for family"""
|
|
end_date = datetime.utcnow()
|
|
start_date = end_date - timedelta(days=days)
|
|
|
|
transactions = self.transaction_repo.get_transactions_by_period(
|
|
family_id, start_date, end_date
|
|
)
|
|
|
|
income = sum(t.amount for t in transactions if t.transaction_type == TransactionType.INCOME)
|
|
expenses = sum(t.amount for t in transactions if t.transaction_type == TransactionType.EXPENSE)
|
|
net = income - expenses
|
|
|
|
return {
|
|
"period_days": days,
|
|
"income": income,
|
|
"expenses": expenses,
|
|
"net": net,
|
|
"average_daily_expense": expenses / days if days > 0 else 0,
|
|
"transaction_count": len(transactions),
|
|
}
|
|
|
|
def delete_transaction(self, transaction_id: int) -> bool:
|
|
"""Delete transaction and rollback balance"""
|
|
transaction = self.transaction_repo.get_by_id(transaction_id)
|
|
if transaction:
|
|
# Rollback balance
|
|
if transaction.transaction_type == TransactionType.EXPENSE:
|
|
self.account_repo.update_balance(transaction.account_id, transaction.amount)
|
|
elif transaction.transaction_type == TransactionType.INCOME:
|
|
self.account_repo.update_balance(transaction.account_id, -transaction.amount)
|
|
|
|
# Delete transaction
|
|
return self.transaction_repo.delete(transaction_id)
|
|
return False
|