fin-hub/src/fin_hub/models/budget.py
tegwick 09ef7fa967 feat: bound AI-plan work to resource-control and stop zeroing missing tokens
Record the FIN-WP-0007 split after checking resource-control: they keep
intelligence resource identity and provision-level class I metering;
session tokens stay with State Hub; work-effectiveness is a reporting
join. Missing token counts are now unknown, not zero.
2026-08-15 19:01:45 +02:00

77 lines
No EOL
4 KiB
Python

"""Financial viability models — initial schema for CUST-WP-0025-T23."""
from __future__ import annotations
import uuid
from datetime import date, datetime
from sqlalchemy import Date, DateTime, Float, Integer, String, Text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from hub_core.models.base import Base, TimestampMixin
class Budget(Base, TimestampMixin):
__tablename__ = "fin_budgets"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
period_start: Mapped[date] = mapped_column(Date, nullable=False)
period_end: Mapped[date] = mapped_column(Date, nullable=False)
allocated: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
committed: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
spent: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
class Commitment(Base, TimestampMixin):
__tablename__ = "fin_commitments"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
commitment_type: Mapped[str] = mapped_column(String(32), nullable=False)
title: Mapped[str] = mapped_column(String(256), nullable=False)
amount: Mapped[float] = mapped_column(Float, nullable=False)
cadence: Mapped[str] = mapped_column(String(16), nullable=False, default="monthly")
start_date: Mapped[date] = mapped_column(Date, nullable=False)
end_date: Mapped[date | None] = mapped_column(Date, nullable=True)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
class BurnRate(Base, TimestampMixin):
__tablename__ = "fin_burn_rates"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
period_month: Mapped[str] = mapped_column(String(7), nullable=False, index=True)
actual_spend: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
projected_spend: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
class RunwayProjection(Base, TimestampMixin):
__tablename__ = "fin_runway_projections"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
current_balance: Mapped[float] = mapped_column(Float, nullable=False)
monthly_burn: Mapped[float] = mapped_column(Float, nullable=False)
months_remaining: Mapped[float] = mapped_column(Float, nullable=False)
alert_threshold_months: Mapped[float] = mapped_column(Float, nullable=False, default=3.0)
computed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
class TokenSpend(Base, TimestampMixin):
__tablename__ = "fin_token_spends"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
provider: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
model: Mapped[str] = mapped_column(String(64), nullable=False)
tokens_in: Mapped[int | None] = mapped_column(Integer, nullable=True)
tokens_out: Mapped[int | None] = mapped_column(Integer, nullable=True)
cost: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
session_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
recorded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")