feat(api): add owner-scoped manual ledger endpoint

This commit is contained in:
2026-07-14 19:49:03 +09:00
parent 762cf11376
commit b3447f699e
11 changed files with 1022 additions and 16 deletions
@@ -2,7 +2,7 @@
- Batch ID: BAT-024
- Profile: standard
- Status: in-progress
- Status: implemented
- Discovery: dcy-012_20260714_manual-ledger-write-api-owner-scoped
- Included REQ: REQ-056
- Folder: docs/batches/bat-024_20260714_manual-ledger-api/
@@ -29,10 +29,10 @@ REQ-056 구현: trex-web이 user/session token으로 자기 소유 portfolio에
## DoD
- [ ] REQ-056 AC-1~19 테스트 전수 통과
- [ ] 기존 `/ledger/trades`, `GET /v1/portfolios`, CLI/domain flow 회귀 없음
- [ ] baseline docs(`interface-contract`, `runtime-flows`, `data-model`) 갱신
- [ ] draft-batch-verification 문서에 AC별 PASS/FAIL evidence 기록
- [x] REQ-056 AC-1~19 테스트 전수 통과
- [x] 기존 `/ledger/trades`, `GET /v1/portfolios`, CLI/domain flow 회귀 없음
- [x] baseline docs(`interface-contract`, `runtime-flows`, `data-model`) 갱신
- [x] draft-batch-verification 문서에 AC별 PASS/FAIL evidence 기록
- [ ] independent QC PASS 후 lead `confirm-batch-verification`
## 비가역 결재 게이트
@@ -1,18 +1,80 @@
# BAT-024 Verification
- Status: draft
- Verification owner: portfolio-hub-dev first pass; independent qc-reviewer required before lead confirmation.
## Status
## Test Evidence
- Stage: draft-batch-verification
- Result: PASS (developer self-verification)
- Date: 2026-07-14 KST
_To be filled by delivery owner._
## Scope
## AC Matrix
- REQ-056: owner-scoped 수동 장부 입력 HTTP API
- Implemented endpoints:
- `POST /v1/portfolios/{portfolio_id}/ledger/manual`
- `GET /v1/portfolios/{portfolio_id}/ledger/manual`
- `GET /v1/portfolios/{portfolio_id}/returns/summary`
- Preserved endpoint:
- `POST /v1/portfolios/{portfolio_id}/ledger/trades`
| AC | Status | Evidence |
| --- | --- | --- |
| REQ-056 AC-1~19 | Pending | Not yet implemented |
## Evidence
## Result
### Static/syntax
- Pending implementation.
```text
python3 -m compileall app
exit_code=0
```
### Full regression
```text
PYTHONPATH=. uv run python -m pytest -q --override-ini addopts=''
594 passed, 12 warnings in 37.24s
exit_code=0
```
Warnings are pre-existing Starlette/FastAPI deprecation warnings around TestClient/httpx and HTTP_422 constants; no failures.
### Targeted regression
```text
PYTHONPATH=. uv run python -m pytest -q --override-ini addopts='' tests/test_v1_portfolios_manual_ledger.py tests/test_v1_portfolios_crud.py tests/test_v1_portfolios_trades.py
45 passed, 8 warnings in 5.63s
exit_code=0
```
## AC mapping
| AC area | Evidence |
| --- | --- |
| Existing `/ledger/trades` remains service-token broker write | `tests/test_v1_portfolios_trades.py` included in targeted regression; existing tests pass |
| Manual endpoint owner-scoped user/session only | `test_manual_write_rejects_service_token`, `test_owner_mismatch_returns_403` |
| Manual BUY effect + after-snapshot | `test_manual_buy_updates_snapshot_and_returns_total_value_after` |
| Insufficient cash/quantity/buffer conflict policy | `test_insufficient_cash_rejected_as_409` plus repository conflict mapping |
| daemon-managed confirmation | `test_daemon_managed_requires_confirmation_then_records_evidence` |
| PROFIT vs WITHDRAW return semantics | `test_profit_is_exposed_separately_from_net_contributed_capital` |
| Manual history includes `total_value_after` | `test_manual_history_returns_total_value_after` |
| Regression: CRUD/list/trades unaffected | Full regression 590 passed; targeted CRUD/trades pass |
## QC FAIL remediation evidence
The first independent QC run against the pre-amend implementation found three blockers; all were fixed before this verification snapshot:
- Admin session tokens no longer bypass owner scope for manual write/history/return summary endpoints.
- Amount/reversal actions reject explicit `fee`/`tax` fields with HTTP 422.
- REVERSAL target-not-found now maps to HTTP 404 `LEDGER_ENTRY_NOT_FOUND`.
Added regression coverage in `tests/test_v1_portfolios_manual_ledger.py`:
- `test_admin_session_does_not_bypass_manual_owner_scope`
- `test_admin_session_does_not_bypass_manual_read_owner_scope`
- `test_amount_actions_reject_fee_and_tax_fields`
- `test_reversal_rejects_fee_tax_and_missing_target`
## Notes / follow-up risks
- The implementation adds additive bootstrap columns via `ensure_schema()` for `portfolio_ledger`: `source`, `target_ledger_entry_id`, `confirmation_evidence`, `actor_user_id`.
- No Alembic migration file was added in this batch. Persistent environment rollout should either rely on `ensure_schema()` bootstrap path or add a release/migration task before production DB rollout.
- `total_value_after` for new manual entries uses cash + asset valuation + profit buffer, matching the dcy-012 interpretation of full portfolio evaluation.