feat: consolidate ledger trade writes into portfolio_ledger
- add ledger consolidation migration and remove ledger_trades usage - return portfolio state after v1 ledger trade writes - deprecate legacy buy/sell endpoints with deprecation headers - add BAT-016, DCY-006, and REQ-036~038 documentation - update repository models and tests for unified ledger entries
This commit is contained in:
+28
-2
@@ -1,9 +1,12 @@
|
||||
"""Portfolio API routes."""
|
||||
|
||||
from typing import NoReturn
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Query, Request, Response, status
|
||||
|
||||
from app.core.auth import AuthContext, Permission, authorize_request
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.core.errors import FeatureNotReadyError
|
||||
from app.repositories.factory import get_portfolio_repository_from_settings
|
||||
from app.schemas.portfolio import (
|
||||
BuyRequest,
|
||||
@@ -49,6 +52,21 @@ def _ownership_required(settings: Settings) -> bool:
|
||||
return settings.db_enabled
|
||||
|
||||
|
||||
def _raise_with_deprecation_header(exc: Exception) -> NoReturn:
|
||||
if isinstance(exc, HTTPException):
|
||||
headers = dict(exc.headers or {})
|
||||
headers["Deprecation"] = "true"
|
||||
exc.headers = headers
|
||||
raise exc
|
||||
if isinstance(exc, FeatureNotReadyError):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
||||
detail=str(exc) or "Feature not implemented yet",
|
||||
headers={"Deprecation": "true"},
|
||||
) from exc
|
||||
raise exc
|
||||
|
||||
|
||||
def _require_owner_context(auth_context: AuthContext) -> str:
|
||||
if auth_context.user_id is None:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Ownership context is required")
|
||||
@@ -155,38 +173,46 @@ def update_portfolio(
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post("/{portfolio_id}/buy", response_model=PortfolioDetailResponse)
|
||||
@router.post("/{portfolio_id}/buy", response_model=PortfolioDetailResponse, deprecated=True)
|
||||
def buy_portfolio_asset(
|
||||
portfolio_id: str,
|
||||
payload: BuyRequest,
|
||||
request: Request,
|
||||
response: Response,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
response.headers["Deprecation"] = "true"
|
||||
auth_context = authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
if _ownership_required(settings):
|
||||
_ensure_portfolio_owned(repository, portfolio_id, auth_context)
|
||||
return service.buy(portfolio_id, payload)
|
||||
except Exception as exc:
|
||||
_raise_with_deprecation_header(exc)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@router.post("/{portfolio_id}/sell", response_model=PortfolioDetailResponse)
|
||||
@router.post("/{portfolio_id}/sell", response_model=PortfolioDetailResponse, deprecated=True)
|
||||
def sell_portfolio_asset(
|
||||
portfolio_id: str,
|
||||
payload: SellRequest,
|
||||
request: Request,
|
||||
response: Response,
|
||||
*,
|
||||
settings: Settings = Depends(get_settings),
|
||||
) -> PortfolioDetailResponse:
|
||||
response.headers["Deprecation"] = "true"
|
||||
auth_context = authorize_request(Permission.TRADE_WRITE, request, settings)
|
||||
service, repository = _get_service(settings)
|
||||
try:
|
||||
if _ownership_required(settings):
|
||||
_ensure_portfolio_owned(repository, portfolio_id, auth_context)
|
||||
return service.sell(portfolio_id, payload)
|
||||
except Exception as exc:
|
||||
_raise_with_deprecation_header(exc)
|
||||
finally:
|
||||
_close_repository(repository)
|
||||
|
||||
|
||||
@@ -583,5 +583,7 @@ def record_ledger_trade_v1(
|
||||
tax_amount=trade.tax_amount,
|
||||
executed_at=trade.executed_at,
|
||||
recorded_at=trade.recorded_at,
|
||||
cash_balance_after=trade.cash_balance_after,
|
||||
asset_quantity_after=trade.asset_quantity_after,
|
||||
status=trade_status,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user