Issue 1: repo scaffold and project structure
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""Holding CRUD endpoints."""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from ten31portal.audit import record_audit
|
||||
from ten31portal.auth import get_current_user, require_writer
|
||||
from ten31portal.database import get_session
|
||||
from ten31portal.models import Entity, Holding, Position, User
|
||||
from ten31portal.schemas import HoldingCreate, HoldingResponse, HoldingUpdate
|
||||
|
||||
router = APIRouter(tags=["holdings"])
|
||||
|
||||
|
||||
@router.get("/api/entities/{entity_id}/holdings")
|
||||
def list_holdings(
|
||||
entity_id: int,
|
||||
user: User = Depends(get_current_user),
|
||||
session: Session = Depends(get_session),
|
||||
) -> list[HoldingResponse]:
|
||||
entity = session.get(Entity, entity_id)
|
||||
if entity is None:
|
||||
raise HTTPException(status_code=404, detail="Entity not found")
|
||||
rows = session.exec(select(Holding).where(Holding.entity_id == entity_id)).all()
|
||||
return [HoldingResponse.model_validate(r, from_attributes=True) for r in rows]
|
||||
|
||||
|
||||
@router.post("/api/entities/{entity_id}/holdings", status_code=201)
|
||||
def create_holding(
|
||||
entity_id: int,
|
||||
body: HoldingCreate,
|
||||
user: User = Depends(require_writer),
|
||||
session: Session = Depends(get_session),
|
||||
) -> HoldingResponse:
|
||||
entity = session.get(Entity, entity_id)
|
||||
if entity is None:
|
||||
raise HTTPException(status_code=404, detail="Entity not found")
|
||||
holding = Holding(entity_id=entity_id, company_name=body.company_name)
|
||||
session.add(holding)
|
||||
session.flush()
|
||||
record_audit(session, user.id, "create", "holding", holding.id, {"entity_id": entity_id, **body.model_dump()})
|
||||
session.commit()
|
||||
session.refresh(holding)
|
||||
return HoldingResponse.model_validate(holding, from_attributes=True)
|
||||
|
||||
|
||||
@router.patch("/api/holdings/{holding_id}")
|
||||
def update_holding(
|
||||
holding_id: int,
|
||||
body: HoldingUpdate,
|
||||
user: User = Depends(require_writer),
|
||||
session: Session = Depends(get_session),
|
||||
) -> HoldingResponse:
|
||||
holding = session.get(Holding, holding_id)
|
||||
if holding is None:
|
||||
raise HTTPException(status_code=404, detail="Holding not found")
|
||||
changes = body.model_dump(exclude_unset=True)
|
||||
for key, val in changes.items():
|
||||
setattr(holding, key, val)
|
||||
session.add(holding)
|
||||
session.flush()
|
||||
record_audit(session, user.id, "update", "holding", holding.id, changes)
|
||||
session.commit()
|
||||
session.refresh(holding)
|
||||
return HoldingResponse.model_validate(holding, from_attributes=True)
|
||||
|
||||
|
||||
@router.delete("/api/holdings/{holding_id}")
|
||||
def delete_holding(
|
||||
holding_id: int,
|
||||
user: User = Depends(require_writer),
|
||||
session: Session = Depends(get_session),
|
||||
) -> dict[str, str]:
|
||||
holding = session.get(Holding, holding_id)
|
||||
if holding is None:
|
||||
raise HTTPException(status_code=404, detail="Holding not found")
|
||||
positions = session.exec(select(Position).where(Position.holding_id == holding_id)).all()
|
||||
if positions:
|
||||
raise HTTPException(status_code=409, detail="Cannot delete holding with existing positions. Remove positions first.")
|
||||
record_audit(session, user.id, "delete", "holding", holding.id, {"company_name": holding.company_name})
|
||||
session.delete(holding)
|
||||
session.commit()
|
||||
return {"status": "deleted"}
|
||||
Reference in New Issue
Block a user