fix: add unified ledger amount and enforce manual decimal precision

Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-09-09 02:23:32 +00:00
committed by GitHub
co-authored by genichin
parent ccde81b7a0
commit a71c91f6ef
8 changed files with 173 additions and 55 deletions
@@ -10,6 +10,7 @@ from __future__ import annotations
import os
from datetime import datetime, timezone
from decimal import Decimal
from urllib.parse import unquote, urlparse
from uuid import uuid4
@@ -271,3 +272,43 @@ def test_broker_and_manual_ledger_writes_regress_after_real_upgrade(
)
assert cur.fetchone() == ("manual", "bat025-user", "synthetic-local-fixture")
repo.close()
def test_dividend_precision_and_reversal_round_trip_on_real_postgres(
isolated_settings: Settings,
) -> None:
"""DIVIDEND min unit persists and REVERSAL removes the same amount exactly."""
upgrade_to_head(isolated_settings)
repo = _repo(isolated_settings)
portfolio_id = _seed_portfolio(repo, "dividend-precision")
dividend = repo.record_manual_ledger(
portfolio_id,
action="DIVIDEND",
amount=Decimal("0.00000001"),
tax=Decimal("0.00000001"),
executed_at=datetime(2026, 9, 1, 0, 0, 0, tzinfo=timezone.utc),
note="precision-test",
)
assert dividend.price == 0.00000001
assert dividend.tax_amount == 0.00000001
assert dividend.profit_buffer_after == 0.00000001
with repo._connect().cursor() as cur:
cur.execute(
"SELECT price::text, tax_amount::text, profit_buffer_after::text FROM portfolio_ledger WHERE ledger_entry_id = %s",
(dividend.ledger_entry_id,),
)
assert cur.fetchone() == ("0.00000001", "0.00000001", "0.00000001")
reversal = repo.record_manual_ledger(
portfolio_id,
action="REVERSAL",
target_ledger_entry_id=dividend.ledger_entry_id,
)
assert reversal.profit_buffer_after == 0.0
with repo._connect().cursor() as cur:
cur.execute("SELECT profit_buffer::text FROM portfolios WHERE portfolio_id = %s", (portfolio_id,))
assert cur.fetchone()[0] == "0"
repo.close()
+36
View File
@@ -500,6 +500,30 @@ def test_dividend_requires_executed_at_and_rejects_fee_field() -> None:
assert invalid_fee.status_code == 422
def test_dividend_rejects_sub_minimum_numeric_precision() -> None:
repo = _FakeRepo(_portfolio())
client = _make_client(repo)
too_precise = client.post(
f"/v1/portfolios/{_PORTFOLIO_ID}/ledger/manual",
json={"action": "DIVIDEND", "amount": "0.000000004", "executed_at": "2026-09-01"},
headers=_auth(),
)
assert too_precise.status_code == 422
def test_dividend_rejects_amount_exceeding_numeric_20_8() -> None:
repo = _FakeRepo(_portfolio())
client = _make_client(repo)
too_large = client.post(
f"/v1/portfolios/{_PORTFOLIO_ID}/ledger/manual",
json={"action": "DIVIDEND", "amount": "1000000000000.00000000", "executed_at": "2026-09-01"},
headers=_auth(),
)
assert too_large.status_code == 422
def test_dividend_duplicate_requires_confirmation() -> None:
repo = _FakeRepo(_portfolio())
client = _make_client(repo)
@@ -571,12 +595,24 @@ def test_unified_ledger_includes_dividend_fields() -> None:
item = resp.json()["items"][0]
assert item["source"] == "manual"
assert item["action"] == "DIVIDEND"
assert item["amount"] == 33.0
assert item["price"] == 33.0
assert item["executed_at"] == "2026-09-01T00:00:00Z"
assert item["tax_amount"] == 3.0
assert item["note"] == "Q3"
assert item["profit_buffer_after"] == created.json()["profit_buffer_after"]
def test_unified_ledger_openapi_contains_nullable_amount() -> None:
repo = _FakeRepo(_portfolio())
client = _make_client(repo)
response = client.get("/openapi.json")
assert response.status_code == 200
schemas = response.json()["components"]["schemas"]
assert "amount" in schemas["LedgerHistoryItem"]["properties"]
def test_reversal_rejects_fee_tax_and_missing_target() -> None:
repo = _FakeRepo(_portfolio())
client = _make_client(repo)