generated from coulomb/repo-seed
Add liquidity tracking, budget, and platform cost history
Restore operator platform costs (Bubble, domains, Stripe) with monthly history from March 2025 and member payments from November 2025. Track €1,000 starting budget, cumulative burn, and remaining liquidity in the economics dashboard. Document LQ requirements in REQUIREMENTS.md.
This commit is contained in:
@@ -5,8 +5,10 @@ from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
from .models import (
|
||||
Budget,
|
||||
CostEntry,
|
||||
MembershipRecord,
|
||||
MonthlyPlatformCost,
|
||||
PricingModel,
|
||||
Product,
|
||||
RevenueEntry,
|
||||
@@ -37,6 +39,15 @@ def load_product(data_dir: Path | None = None) -> Product:
|
||||
)
|
||||
|
||||
|
||||
def load_budget(data_dir: Path | None = None) -> Budget:
|
||||
raw = _read_json((data_dir or default_data_dir()) / "budget.json")
|
||||
return Budget(
|
||||
currency=raw["currency"],
|
||||
initial_budget=_money(raw["initial_budget"]),
|
||||
started=raw["started"],
|
||||
)
|
||||
|
||||
|
||||
def load_pricing_models(data_dir: Path | None = None) -> list[PricingModel]:
|
||||
raw = _read_json((data_dir or default_data_dir()) / "pricing-models.json")
|
||||
return [
|
||||
@@ -54,9 +65,8 @@ def load_pricing_models(data_dir: Path | None = None) -> list[PricingModel]:
|
||||
]
|
||||
|
||||
|
||||
def load_costs(data_dir: Path | None = None) -> tuple[str, list[CostEntry], dict[str, Decimal]]:
|
||||
raw = _read_json((data_dir or default_data_dir()) / "costs.json")
|
||||
entries = [
|
||||
def _parse_cost_entries(items: list[dict]) -> list[CostEntry]:
|
||||
return [
|
||||
CostEntry(
|
||||
id=item["id"],
|
||||
name=item["name"],
|
||||
@@ -66,10 +76,28 @@ def load_costs(data_dir: Path | None = None) -> tuple[str, list[CostEntry], dict
|
||||
cadence=item["cadence"],
|
||||
allocation=item["allocation"],
|
||||
)
|
||||
for item in raw["entries"]
|
||||
for item in items
|
||||
]
|
||||
|
||||
|
||||
def load_cost_rate_card(data_dir: Path | None = None) -> tuple[list[CostEntry], dict[str, Decimal]]:
|
||||
raw = _read_json((data_dir or default_data_dir()) / "costs.json")
|
||||
items = raw.get("rate_card", raw.get("entries", []))
|
||||
fx = {pair: _money(rate) for pair, rate in raw.get("fx_rates", {}).items()}
|
||||
return raw.get("period", ""), entries, fx
|
||||
return _parse_cost_entries(items), fx
|
||||
|
||||
|
||||
def load_monthly_platform_costs(data_dir: Path | None = None) -> list[MonthlyPlatformCost]:
|
||||
raw = _read_json((data_dir or default_data_dir()) / "costs.json")
|
||||
return [
|
||||
MonthlyPlatformCost(
|
||||
period=item["period"],
|
||||
platform_cost=_money(item["platform_cost"]),
|
||||
active_members=item["active_members"],
|
||||
gross_revenue=_money(item["gross_revenue"]),
|
||||
)
|
||||
for item in raw.get("monthly_history", [])
|
||||
]
|
||||
|
||||
|
||||
def load_revenue(data_dir: Path | None = None) -> list[RevenueEntry]:
|
||||
@@ -100,4 +128,8 @@ def load_membership(data_dir: Path | None = None) -> list[MembershipRecord]:
|
||||
churned_at=item.get("churned_at"),
|
||||
)
|
||||
for item in raw["members"]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def latest_period(monthly_costs: list[MonthlyPlatformCost]) -> str:
|
||||
return max(item.period for item in monthly_costs)
|
||||
Reference in New Issue
Block a user