feat(req-013): add portfolio-list-user-required requirement and batch

- Add REQ-013: Portfolio list filtered by user requirement with acceptance criteria
- Add DCY-003 discovery document for portfolio list user filtering
- Create BAT-003 batch with planning, design, implementation, and verification
- Enhance app/cli/commands/portfolio.py with user-based list filtering
- Add comprehensive tests for portfolio list CLI command with user filtering
- Update docs indices (discovery, srs, batches)
- Improve README with latest capability status
This commit is contained in:
2026-05-02 13:22:41 +00:00
parent b4d964253b
commit 2f686f79ce
12 changed files with 699 additions and 13 deletions
+26 -5
View File
@@ -51,6 +51,18 @@ def _with_service(ctx: typer.Context, callback):
repository.close()
def _with_service_and_repo(ctx: typer.Context, callback):
app_context = ctx.obj
repository = app_context.repository_factory(app_context.settings)
service = PortfolioService(repository)
try:
result = execute_command(app_context.console, callback=lambda: callback(service, repository, app_context))
return result
finally:
if callable(getattr(type(repository), "close", None)):
repository.close()
def _confirm(force: bool, message: str) -> None:
if force:
return
@@ -62,22 +74,31 @@ def _confirm(force: bool, message: str) -> None:
@portfolio_app.command("list")
def list_portfolios(
ctx: typer.Context,
user: str | None = typer.Option(None, "--user", help="Filter by username (required)."),
broker: str | None = typer.Option(None, help="Filter by broker."),
status: str | None = typer.Option(None, help="Filter by status."),
include_tags: list[str] | None = typer.Option(None, "--include-tag", help="Include tag filter. Repeatable."),
exclude_tags: list[str] | None = typer.Option(None, "--exclude-tag", help="Exclude tag filter. Repeatable."),
match_mode: str | None = typer.Option(None, "--match-mode", help="AND or OR for include tags."),
) -> None:
detail = _with_service(
ctx,
lambda service, app_context: service.list_portfolios(
if user is None:
ctx.obj.console.print("Error: --user 옵션은 필수입니다.")
raise typer.Exit(code=1)
def _callback(service, repository, app_context):
owner = repository.get_user_by_username(user)
if owner is None:
raise ValueError(f"사용자를 찾을 수 없습니다: {user}")
return service.list_portfolios(
owner_user_id=owner.id,
broker=broker,
status=status,
include_tags=include_tags,
exclude_tags=exclude_tags,
match_mode=match_mode,
),
)
)
detail = _with_service_and_repo(ctx, _callback)
render_output(ctx.obj.console, ctx.obj.output_format, detail, empty_message="No portfolios found.")