Cumulative checkpoint since 0.2.26:
- 0.2.27/28: entity valuation-history table; investor gain/loss = NAV +
distributions vs paid-in
- 0.2.29: Reset Fund Partners (endpoint, Partners-tab button, CLI, action)
- 0.2.30: "Current Capital Balance" label, %-only gain/loss
- 0.2.31: Management Entities rename, Carry Vehicle type, chart
distributions-line gate
- 0.2.32: LP-facing polish pass
* Ten31 brand palette from the logo (navy/mint); orange retired
* portfolio summary card across funds; gain labeled "net of paid-in"
* whole-dollar headline figures; "History · N quarters" toggle
* documents grouped by year with a "New" badge (users.docs_seen_at)
* eNAV-created members start on default password with login enabled;
enable-investor-logins CLI + StartOS action for existing accounts
* password minimum raised to 8 chars; login help line (Portal@ten31.xyz)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
"""Clearing a fund's partners removes only that fund's statements + access grants,
|
|
never the investor accounts (which may belong to other funds)."""
|
|
|
|
from datetime import date
|
|
|
|
from ten31portal.models import (
|
|
CapitalAccountStatement, Entity, EntityAccess, EntityType, User, UserRole,
|
|
)
|
|
|
|
|
|
def _make_fund(session, name):
|
|
e = Entity(name=name, type=EntityType.fund)
|
|
session.add(e)
|
|
session.commit()
|
|
session.refresh(e)
|
|
return e
|
|
|
|
|
|
def _add_partner(session, entity_id, user_id, ending):
|
|
session.add(EntityAccess(user_id=user_id, entity_id=entity_id))
|
|
session.add(CapitalAccountStatement(
|
|
entity_id=entity_id, investor_user_id=user_id, as_of_date=date(2025, 12, 31),
|
|
commitment_cents=0, beginning_balance_cents=0, contributions_cents=0,
|
|
distributions_cents=0, ending_balance_cents=ending,
|
|
))
|
|
session.commit()
|
|
|
|
|
|
def test_clear_partners_scoped_to_one_fund(auth_client, session):
|
|
from tests.conftest import make_user
|
|
|
|
fund2 = _make_fund(session, "Low Time Preference Fund II, LLC")
|
|
fund3 = _make_fund(session, "Low Time Preference Fund III, LP")
|
|
lp = make_user(session, username="lp1", role=UserRole.investor, name="LP One")
|
|
# Same investor is a partner in BOTH funds (the real-world case that caused the mixup).
|
|
_add_partner(session, fund2.id, lp.id, 1_000_00)
|
|
_add_partner(session, fund3.id, lp.id, 2_000_00)
|
|
|
|
resp = auth_client.delete(f"/api/entities/{fund3.id}/partners")
|
|
assert resp.status_code == 200, resp.text
|
|
assert resp.json() == {"statements": 1, "access_grants": 1}
|
|
|
|
# Fund III is wiped of partners...
|
|
assert session.exec(
|
|
CapitalAccountStatement.__table__.select().where(
|
|
CapitalAccountStatement.entity_id == fund3.id
|
|
)
|
|
).first() is None
|
|
assert session.exec(
|
|
EntityAccess.__table__.select().where(EntityAccess.entity_id == fund3.id)
|
|
).first() is None
|
|
assert auth_client.get(f"/api/entities/{fund3.id}/partners").json() == []
|
|
|
|
# ...but Fund II keeps its partner, and the investor account still exists.
|
|
assert len(auth_client.get(f"/api/entities/{fund2.id}/partners").json()) == 1
|
|
assert session.get(User, lp.id) is not None
|
|
|
|
|
|
def test_clear_partners_missing_entity_404(auth_client):
|
|
assert auth_client.delete("/api/entities/99999/partners").status_code == 404
|