generated from coulomb/repo-seed
Add file-based Bubble, Stripe, and OpenRouter importers; usage attribution, cost allocation, pricing simulator, credit wallets, and recommendations in the dashboard API. Document whynot-design UI workflow and archive the finished workplan with all ten tasks marked done.
48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
from __future__ import annotations
|
|
|
|
from decimal import Decimal, ROUND_HALF_UP
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from .load import _read_json, default_data_dir
|
|
|
|
TWOPLACES = Decimal("0.01")
|
|
|
|
|
|
def _money(value: Decimal) -> Decimal:
|
|
return value.quantize(TWOPLACES, rounding=ROUND_HALF_UP)
|
|
|
|
|
|
def load_credit_wallets(data_dir=None) -> dict[str, Any]:
|
|
root = data_dir or default_data_dir()
|
|
path = Path(root) / "credit_wallets.json"
|
|
if not path.exists():
|
|
return {"version": 1, "currency": "EUR", "wallets": []}
|
|
return _read_json(path)
|
|
|
|
|
|
def build_credit_summary(raw: dict, usage_by_member: dict[str, Decimal], period: str) -> dict:
|
|
wallets = []
|
|
for item in raw.get("wallets", []):
|
|
member_id = item["member_id"]
|
|
allowance = Decimal(str(item.get("monthly_allowance_eur", "0")))
|
|
used = usage_by_member.get(member_id, Decimal(str(item.get("used_eur", "0"))))
|
|
remaining = max(Decimal("0"), allowance - used)
|
|
wallets.append(
|
|
{
|
|
"member_id": member_id,
|
|
"period": period,
|
|
"monthly_allowance_eur": _money(allowance),
|
|
"used_eur": _money(used),
|
|
"remaining_eur": _money(remaining),
|
|
"overage_eur": _money(max(Decimal("0"), used - allowance)),
|
|
}
|
|
)
|
|
|
|
return {
|
|
"period": period,
|
|
"currency": raw.get("currency", "EUR"),
|
|
"wallet_count": len(wallets),
|
|
"wallets": wallets,
|
|
"notes": "Observatory-only credit accounting; no customer billing in MVP.",
|
|
} |