Files
finance/backend/app/spaces/service.py
T

61 lines
2.1 KiB
Python

import uuid
from sqlalchemy.orm import Session
from app.audit import service as audit
from app.db.session import set_space_context
from app.errors import conflict, forbidden, not_found
from app.identity.models import User
from app.spaces.models import FinancialSpace, Membership
def create_space(db: Session, *, user: User, name: str) -> FinancialSpace:
space = FinancialSpace(name=name)
db.add(space)
db.flush()
set_space_context(db, space.id)
db.add(Membership(space_id=space.id, user_id=user.id, role="owner"))
from app.ledger.savings import ensure_savings_resources
ensure_savings_resources(db, space_id=space.id)
audit.record(db, event_type="space.created", space_id=space.id, user_id=user.id)
return space
def get_membership(db: Session, *, space_id: uuid.UUID, user_id: uuid.UUID) -> Membership:
membership = db.query(Membership).filter_by(space_id=space_id, user_id=user_id).first()
if membership is None:
raise not_found("SPACE_NOT_FOUND", "Financial space not found")
return membership
def get_space_for_member(
db: Session, *, space_id: uuid.UUID, user: User
) -> tuple[FinancialSpace, Membership]:
space = db.get(FinancialSpace, space_id)
if space is None:
raise not_found("SPACE_NOT_FOUND", "Financial space not found")
membership = get_membership(db, space_id=space_id, user_id=user.id)
if space.status == "blocked":
raise conflict(
"SPACE_BLOCKED",
"Financial space is blocked pending deletion",
)
return space, membership
def require_owner(membership: Membership) -> None:
if membership.role != "owner":
raise forbidden("SPACE_OWNER_REQUIRED", "Owner role required")
def list_spaces_for_user(db: Session, *, user: User) -> list[tuple[FinancialSpace, Membership]]:
rows = (
db.query(FinancialSpace, Membership)
.join(Membership, Membership.space_id == FinancialSpace.id)
.filter(Membership.user_id == user.id)
.order_by(FinancialSpace.created_at)
.all()
)
return [(space, membership) for space, membership in rows]