generated from coulomb/repo-seed
Refactor economics to expense-record ledger with correct Bubble cost
Replace pre-aggregated costs.json with expense_records.json (48 line-item records) and payment_records.json. All monthly and cumulative totals are computed deterministically in observatory/ledger.py. Correct Bubble.io to $32/mo (since Feb 2025) — infrastructure €69.44/mo not €72.20.
This commit is contained in:
@@ -9,26 +9,27 @@ from .load import (
|
||||
default_data_dir,
|
||||
latest_period,
|
||||
load_budget,
|
||||
load_expense_records,
|
||||
load_membership,
|
||||
load_monthly_platform_costs,
|
||||
load_monthly_ledger,
|
||||
load_payment_records,
|
||||
load_pricing_models,
|
||||
load_product,
|
||||
load_revenue,
|
||||
)
|
||||
from .models import EconomicsSnapshot, LiquiditySummary, MonthlyPlatformCost, PricingModel, Product, RevenueEntry
|
||||
from .models import EconomicsSnapshot, LiquiditySummary, MonthlyPlatformCost, PaymentRecord, PricingModel, Product
|
||||
|
||||
|
||||
def _history_rows(
|
||||
monthly_costs: list[MonthlyPlatformCost],
|
||||
revenue_entries: list[RevenueEntry],
|
||||
payments: list[PaymentRecord],
|
||||
through_period: str,
|
||||
) -> str:
|
||||
revenue_by_period = {entry.period: entry for entry in revenue_entries}
|
||||
payment_by_period = {record.period: record for record in payments}
|
||||
lines: list[str] = []
|
||||
for month in sorted(monthly_costs, key=lambda item: item.period):
|
||||
if month.period > through_period:
|
||||
continue
|
||||
payment = revenue_by_period.get(month.period)
|
||||
payment = payment_by_period.get(month.period)
|
||||
net_payment = payment.net_amount if payment else Decimal("0")
|
||||
period_net = net_payment - month.infrastructure_cost
|
||||
lines.append(
|
||||
@@ -45,14 +46,15 @@ def render_dashboard(
|
||||
snapshot: EconomicsSnapshot,
|
||||
liquidity: LiquiditySummary,
|
||||
monthly_costs: list[MonthlyPlatformCost],
|
||||
revenue_entries: list[RevenueEntry],
|
||||
payments: list[PaymentRecord],
|
||||
expense_count: int,
|
||||
) -> str:
|
||||
active = next(m for m in models if m.id == product.active_pricing_model_id)
|
||||
registry_lines = "\n".join(
|
||||
f"| {model.id} | {model.name} | {model.model_type} | {model.status} |"
|
||||
for model in models
|
||||
)
|
||||
history_lines = _history_rows(monthly_costs, revenue_entries, snapshot.period)
|
||||
history_lines = _history_rows(monthly_costs, payments, snapshot.period)
|
||||
budget_state = (
|
||||
"over budget"
|
||||
if liquidity.remaining_budget < Decimal("0")
|
||||
@@ -66,7 +68,9 @@ def render_dashboard(
|
||||
**Active pricing model:** {active.name} ({active.access_fee_amount} {active.currency}/{active.access_fee_cadence})
|
||||
|
||||
> Platform costs accrue to the operator. Customer cost-pass-through billing is
|
||||
> **not active** in MVP — members pay subscription only.
|
||||
> **not active** in MVP — members pay subscription only. All totals are computed
|
||||
> programmatically from expense and payment record ledgers ({expense_count} expense
|
||||
> records).
|
||||
|
||||
## Key Metrics (current period)
|
||||
|
||||
@@ -114,9 +118,8 @@ _Revenue source: {snapshot.revenue_source}_
|
||||
|
||||
- Product model (`data/product.json`)
|
||||
- Budget (`data/budget.json`)
|
||||
- Pricing model registry (`data/pricing-models.json`)
|
||||
- Platform costs (`data/costs.json`)
|
||||
- Member payments (`data/revenue.json`)
|
||||
- Expense records (`data/expense_records.json`) — source of truth for costs
|
||||
- Payment records (`data/payment_records.json`)
|
||||
- Membership (`data/membership.json`)
|
||||
- Requirements (`REQUIREMENTS.md`)
|
||||
"""
|
||||
@@ -128,13 +131,16 @@ def generate_dashboard(data_dir: Path | None = None, period: str | None = None)
|
||||
budget = load_budget(root)
|
||||
models = load_pricing_models(root)
|
||||
members = load_membership(root)
|
||||
revenue = load_revenue(root)
|
||||
monthly_costs = load_monthly_platform_costs(root)
|
||||
payments = load_payment_records(root)
|
||||
expenses = load_expense_records(root)
|
||||
monthly_costs = load_monthly_ledger(root)
|
||||
target_period = period or latest_period(monthly_costs)
|
||||
|
||||
snapshot = build_snapshot(target_period, product, models, members, revenue, monthly_costs)
|
||||
liquidity = build_liquidity_summary(budget, revenue, monthly_costs, target_period)
|
||||
return render_dashboard(product, models, snapshot, liquidity, monthly_costs, revenue)
|
||||
snapshot = build_snapshot(target_period, product, models, members, payments, monthly_costs)
|
||||
liquidity = build_liquidity_summary(budget, payments, monthly_costs, target_period)
|
||||
return render_dashboard(
|
||||
product, models, snapshot, liquidity, monthly_costs, payments, len(expenses)
|
||||
)
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
|
||||
Reference in New Issue
Block a user