feat: asset catalog API, 태그 DB 기초, 마이그레이션 3건 및 도메인/테스트 갱신 (2026-05-19)

This commit is contained in:
2026-05-19 22:15:55 +00:00
parent e2226e28b6
commit 158929b2d9
33 changed files with 2575 additions and 102 deletions
+13 -3
View File
@@ -145,7 +145,8 @@ def create_portfolio(
ctx: typer.Context,
name: str = typer.Argument(..., help="Portfolio name (user-scoped unique identifier)."),
user: str = typer.Option(..., "--user", help="Owner username (required)."),
asset_id: str = typer.Option(..., "--asset-id", help="Asset UUID from the asset catalog."),
asset_id: str | None = typer.Option(None, "--asset-id", help="Asset UUID from the asset catalog."),
symbol: str | None = typer.Option(None, "--symbol", help="Asset symbol to look up asset_id (alternative to --asset-id)."),
broker: str = typer.Option("kis", help="Broker code."),
status: str = typer.Option("enabled", help="Portfolio status."),
cash_balance: float = typer.Option(0.0, min=0.0, help="Starting cash balance."),
@@ -157,16 +158,24 @@ def create_portfolio(
force: bool = typer.Option(False, "--force", help="Skip confirmation prompt."),
) -> None:
_validate_portfolio_name(name, ctx.obj.console)
_confirm(force, f"Create portfolio {name!r} (asset-id: {asset_id}, user: {user})?")
if asset_id is None and symbol is None:
ctx.obj.console.print("Error: --asset-id 또는 --symbol 중 하나는 필수입니다.")
raise typer.Exit(code=1)
def _callback(service, repository, app_context):
resolved_asset_id = asset_id
if resolved_asset_id is None:
asset = repository.get_asset_by_symbol(symbol)
if asset is None:
raise ValueError(f"symbol에 해당하는 asset을 찾을 수 없습니다: {symbol}")
resolved_asset_id = asset.asset_id
owner = repository.get_user_by_username(user)
if owner is None:
raise ValueError(f"사용자를 찾을 수 없습니다: {user}")
return service.create_portfolio(
CreatePortfolioRequest(
name=name,
asset_id=asset_id,
asset_id=resolved_asset_id,
broker=broker,
status=status,
cash_balance=cash_balance,
@@ -179,6 +188,7 @@ def create_portfolio(
owner_user_id=owner.id,
)
_confirm(force, f"Create portfolio {name!r} (user: {user})?")
detail = _with_service_and_repo(ctx, _callback)
render_output(ctx.obj.console, ctx.obj.output_format, detail)