feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
607
server/src/app/models/commercial.py
Normal file
607
server/src/app/models/commercial.py
Normal file
@@ -0,0 +1,607 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
CheckConstraint,
|
||||
DateTime,
|
||||
ForeignKeyConstraint,
|
||||
Index,
|
||||
Integer,
|
||||
Numeric,
|
||||
String,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
text,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.types import JSON
|
||||
|
||||
from app.db.base_class import Base
|
||||
|
||||
|
||||
def _new_id() -> str:
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
class TenantCommercialPlan(Base):
|
||||
"""租户已协商的商业套餐版本,不与平台内部成本或客户节省事实混用。"""
|
||||
|
||||
__tablename__ = "tenant_commercial_plans"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "id", name="uq_tenant_commercial_plans_tenant_id"),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"plan_code",
|
||||
"version",
|
||||
name="uq_tenant_commercial_plans_tenant_code_version",
|
||||
),
|
||||
CheckConstraint(
|
||||
"pricing_model IN ('subscription', 'usage', 'hybrid', 'pilot', 'custom')",
|
||||
name="ck_tenant_commercial_plans_pricing_model",
|
||||
),
|
||||
CheckConstraint(
|
||||
"billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
|
||||
name="ck_tenant_commercial_plans_billing_interval",
|
||||
),
|
||||
CheckConstraint(
|
||||
"status IN ('draft', 'active', 'retired')",
|
||||
name="ck_tenant_commercial_plans_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"base_fee >= 0 AND included_seats >= 0 AND version >= 1",
|
||||
name="ck_tenant_commercial_plans_values",
|
||||
),
|
||||
CheckConstraint(
|
||||
"length(trim(plan_code)) > 0 AND length(trim(name)) > 0",
|
||||
name="ck_tenant_commercial_plans_keys",
|
||||
),
|
||||
CheckConstraint(
|
||||
"length(trim(currency)) = 3",
|
||||
name="ck_tenant_commercial_plans_currency",
|
||||
),
|
||||
CheckConstraint(
|
||||
"effective_to IS NULL OR effective_to > effective_from",
|
||||
name="ck_tenant_commercial_plans_effective_window",
|
||||
),
|
||||
Index(
|
||||
"uq_tenant_commercial_plans_active_code",
|
||||
"tenant_id",
|
||||
"plan_code",
|
||||
unique=True,
|
||||
postgresql_where=text("status = 'active'"),
|
||||
),
|
||||
Index(
|
||||
"ix_tenant_commercial_plans_tenant_status",
|
||||
"tenant_id",
|
||||
"status",
|
||||
"effective_from",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
plan_code: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(160), nullable=False)
|
||||
pricing_model: Mapped[str] = mapped_column(String(24), nullable=False)
|
||||
billing_interval: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
currency: Mapped[str] = mapped_column(String(3), nullable=False)
|
||||
base_fee: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
|
||||
included_seats: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=0, server_default="0"
|
||||
)
|
||||
overage_enabled: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False, default=False, server_default="false"
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(16), nullable=False, default="draft", server_default="draft"
|
||||
)
|
||||
effective_from: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
effective_to: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
|
||||
contract_terms_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
|
||||
created_by: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
subscriptions = relationship("TenantSubscription", back_populates="plan", passive_deletes=True)
|
||||
|
||||
|
||||
class TenantSubscription(Base):
|
||||
"""租户订阅及当期计费快照,避免套餐后续变化污染历史账期。"""
|
||||
|
||||
__tablename__ = "tenant_subscriptions"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "id", name="uq_tenant_subscriptions_tenant_id"),
|
||||
UniqueConstraint(
|
||||
"tenant_id", "subscription_key", name="uq_tenant_subscriptions_tenant_key"
|
||||
),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"external_provider",
|
||||
"external_subscription_id",
|
||||
name="uq_tenant_subscriptions_external_ref",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "plan_id"],
|
||||
["tenant_commercial_plans.tenant_id", "tenant_commercial_plans.id"],
|
||||
name="fk_tenant_subscriptions_tenant_plan",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
CheckConstraint(
|
||||
"status IN ('trialing', 'active', 'past_due', 'suspended', 'canceled', 'expired')",
|
||||
name="ck_tenant_subscriptions_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
|
||||
name="ck_tenant_subscriptions_billing_interval",
|
||||
),
|
||||
CheckConstraint(
|
||||
"seats > 0 AND base_fee_snapshot >= 0 AND version >= 1",
|
||||
name="ck_tenant_subscriptions_values",
|
||||
),
|
||||
CheckConstraint(
|
||||
"length(trim(subscription_key)) > 0 AND length(trim(currency)) = 3",
|
||||
name="ck_tenant_subscriptions_keys",
|
||||
),
|
||||
CheckConstraint(
|
||||
"current_period_end > current_period_start",
|
||||
name="ck_tenant_subscriptions_period",
|
||||
),
|
||||
CheckConstraint(
|
||||
"ends_at IS NULL OR ends_at > starts_at",
|
||||
name="ck_tenant_subscriptions_contract_window",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(external_provider IS NULL AND external_subscription_id IS NULL) OR "
|
||||
"(external_provider IS NOT NULL AND external_subscription_id IS NOT NULL)",
|
||||
name="ck_tenant_subscriptions_external_pair",
|
||||
),
|
||||
CheckConstraint(
|
||||
"status != 'canceled' OR canceled_at IS NOT NULL",
|
||||
name="ck_tenant_subscriptions_cancellation",
|
||||
),
|
||||
Index(
|
||||
"uq_tenant_subscriptions_current",
|
||||
"tenant_id",
|
||||
unique=True,
|
||||
postgresql_where=text("status IN ('trialing', 'active', 'past_due', 'suspended')"),
|
||||
),
|
||||
Index(
|
||||
"ix_tenant_subscriptions_tenant_status_period",
|
||||
"tenant_id",
|
||||
"status",
|
||||
"current_period_end",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
subscription_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
plan_id: Mapped[str] = mapped_column(String(36), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
starts_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
ends_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
current_period_start: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
current_period_end: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
seats: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
base_fee_snapshot: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
|
||||
currency: Mapped[str] = mapped_column(String(3), nullable=False)
|
||||
billing_interval: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
auto_renew: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False, default=False, server_default="false"
|
||||
)
|
||||
external_provider: Mapped[str | None] = mapped_column(String(60))
|
||||
external_subscription_id: Mapped[str | None] = mapped_column(String(160))
|
||||
canceled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
|
||||
metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
|
||||
created_by: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
plan = relationship("TenantCommercialPlan", back_populates="subscriptions")
|
||||
entitlements = relationship(
|
||||
"CommercialEntitlement", back_populates="subscription", passive_deletes=True
|
||||
)
|
||||
|
||||
|
||||
class CommercialEntitlement(Base):
|
||||
"""订阅的功能权益与可计量配额定义。"""
|
||||
|
||||
__tablename__ = "commercial_entitlements"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "id", name="uq_commercial_entitlements_tenant_id"),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"id",
|
||||
name="uq_commercial_entitlements_tenant_subscription_id",
|
||||
),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"entitlement_key",
|
||||
name="uq_commercial_entitlements_subscription_key",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id"],
|
||||
["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
|
||||
name="fk_commercial_entitlements_tenant_subscription",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
CheckConstraint(
|
||||
"entitlement_type IN ('feature', 'metered', 'unlimited')",
|
||||
name="ck_commercial_entitlements_type",
|
||||
),
|
||||
CheckConstraint(
|
||||
"reset_interval IN ('none', 'monthly', 'quarterly', 'annual', 'contract')",
|
||||
name="ck_commercial_entitlements_reset_interval",
|
||||
),
|
||||
CheckConstraint(
|
||||
"overage_policy IN ('block', 'allow', 'alert')",
|
||||
name="ck_commercial_entitlements_overage_policy",
|
||||
),
|
||||
CheckConstraint(
|
||||
"status IN ('active', 'suspended', 'expired')",
|
||||
name="ck_commercial_entitlements_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"version >= 1 AND (included_quantity IS NULL OR included_quantity >= 0) "
|
||||
"AND (hard_limit_quantity IS NULL OR hard_limit_quantity >= 0)",
|
||||
name="ck_commercial_entitlements_values",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(entitlement_type = 'unlimited' AND included_quantity IS NULL "
|
||||
"AND hard_limit_quantity IS NULL) OR "
|
||||
"(entitlement_type = 'feature' AND included_quantity IN (0, 1) "
|
||||
"AND (hard_limit_quantity IS NULL OR hard_limit_quantity IN (0, 1))) OR "
|
||||
"(entitlement_type = 'metered' AND included_quantity IS NOT NULL "
|
||||
"AND (hard_limit_quantity IS NULL OR hard_limit_quantity >= included_quantity))",
|
||||
name="ck_commercial_entitlements_quota_shape",
|
||||
),
|
||||
CheckConstraint(
|
||||
"length(trim(entitlement_key)) > 0 AND length(trim(metric_key)) > 0 "
|
||||
"AND length(trim(unit)) > 0",
|
||||
name="ck_commercial_entitlements_keys",
|
||||
),
|
||||
CheckConstraint(
|
||||
"effective_to IS NULL OR effective_to > effective_from",
|
||||
name="ck_commercial_entitlements_effective_window",
|
||||
),
|
||||
Index(
|
||||
"ix_commercial_entitlements_subscription_status",
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"status",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
subscription_id: Mapped[str] = mapped_column(String(36), nullable=False)
|
||||
entitlement_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
metric_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
entitlement_type: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
unit: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||
included_quantity: Mapped[Decimal | None] = mapped_column(Numeric(20, 6))
|
||||
hard_limit_quantity: Mapped[Decimal | None] = mapped_column(Numeric(20, 6))
|
||||
reset_interval: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
overage_policy: Mapped[str] = mapped_column(String(16), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(16), nullable=False)
|
||||
effective_from: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
effective_to: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
|
||||
config_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
subscription = relationship("TenantSubscription", back_populates="entitlements")
|
||||
|
||||
|
||||
class UsageMeterEvent(Base):
|
||||
"""追加只读的客户用量事实;同一来源幂等键只能产生一条事件。"""
|
||||
|
||||
__tablename__ = "usage_meter_events"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "id", name="uq_usage_meter_events_tenant_id"),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"id",
|
||||
name="uq_usage_meter_events_tenant_subscription_id",
|
||||
),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"entitlement_id",
|
||||
"id",
|
||||
name="uq_usage_meter_events_entitlement_id",
|
||||
),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"source_system",
|
||||
"idempotency_key",
|
||||
name="uq_usage_meter_events_source_request",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id"],
|
||||
["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
|
||||
name="fk_usage_meter_events_tenant_subscription",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id", "entitlement_id"],
|
||||
[
|
||||
"commercial_entitlements.tenant_id",
|
||||
"commercial_entitlements.subscription_id",
|
||||
"commercial_entitlements.id",
|
||||
],
|
||||
name="fk_usage_meter_events_tenant_entitlement",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id", "billing_period_id"],
|
||||
[
|
||||
"commercial_billing_periods.tenant_id",
|
||||
"commercial_billing_periods.subscription_id",
|
||||
"commercial_billing_periods.id",
|
||||
],
|
||||
name="fk_usage_meter_events_tenant_billing_period",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id", "entitlement_id", "reversal_of_event_id"],
|
||||
[
|
||||
"usage_meter_events.tenant_id",
|
||||
"usage_meter_events.subscription_id",
|
||||
"usage_meter_events.entitlement_id",
|
||||
"usage_meter_events.id",
|
||||
],
|
||||
name="fk_usage_meter_events_tenant_reversal",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
CheckConstraint(
|
||||
"event_type IN ('usage', 'credit', 'adjustment', 'reversal')",
|
||||
name="ck_usage_meter_events_type",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(event_type = 'usage' AND quantity > 0) OR "
|
||||
"(event_type = 'credit' AND quantity < 0) OR "
|
||||
"(event_type IN ('adjustment', 'reversal') AND quantity <> 0)",
|
||||
name="ck_usage_meter_events_quantity",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(event_type = 'reversal' AND reversal_of_event_id IS NOT NULL) OR "
|
||||
"(event_type != 'reversal' AND reversal_of_event_id IS NULL)",
|
||||
name="ck_usage_meter_events_reversal",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(subject_type IS NULL AND subject_id IS NULL) OR "
|
||||
"(subject_type IS NOT NULL AND subject_id IS NOT NULL)",
|
||||
name="ck_usage_meter_events_subject_pair",
|
||||
),
|
||||
CheckConstraint(
|
||||
"actor_type IN ('system', 'user', 'integration', 'admin')",
|
||||
name="ck_usage_meter_events_actor_type",
|
||||
),
|
||||
CheckConstraint(
|
||||
"length(trim(metric_key)) > 0 AND length(trim(unit)) > 0 "
|
||||
"AND length(trim(period_key)) > 0 AND length(trim(source_system)) > 0 "
|
||||
"AND length(trim(quota_period_key)) > 0 "
|
||||
"AND length(trim(idempotency_key)) > 0 "
|
||||
"AND length(trim(request_fingerprint)) > 0",
|
||||
name="ck_usage_meter_events_keys",
|
||||
),
|
||||
Index(
|
||||
"ix_usage_meter_events_quota_window",
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"metric_key",
|
||||
"quota_period_key",
|
||||
"occurred_at",
|
||||
),
|
||||
Index(
|
||||
"ix_usage_meter_events_billing_period",
|
||||
"tenant_id",
|
||||
"billing_period_id",
|
||||
"occurred_at",
|
||||
),
|
||||
Index(
|
||||
"ix_usage_meter_events_correlation",
|
||||
"tenant_id",
|
||||
"correlation_id",
|
||||
"occurred_at",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
subscription_id: Mapped[str] = mapped_column(String(36), nullable=False)
|
||||
entitlement_id: Mapped[str] = mapped_column(String(36), nullable=False)
|
||||
billing_period_id: Mapped[str] = mapped_column(String(36), nullable=False)
|
||||
event_type: Mapped[str] = mapped_column(String(16), nullable=False)
|
||||
metric_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
quantity: Mapped[Decimal] = mapped_column(Numeric(20, 6), nullable=False)
|
||||
unit: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||
period_key: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
quota_period_key: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
source_system: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False)
|
||||
request_fingerprint: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
reversal_of_event_id: Mapped[str | None] = mapped_column(String(36))
|
||||
subject_type: Mapped[str | None] = mapped_column(String(60))
|
||||
subject_id: Mapped[str | None] = mapped_column(String(160))
|
||||
actor_type: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
actor_id: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
correlation_id: Mapped[str | None] = mapped_column(String(120))
|
||||
trace_id: Mapped[str | None] = mapped_column(String(120))
|
||||
metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
|
||||
recorded_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
|
||||
|
||||
class CommercialCostEvent(Base):
|
||||
"""平台内部成本事实;物理上独立于客户节省、价值机会和价值实现。"""
|
||||
|
||||
__tablename__ = "commercial_cost_events"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "id", name="uq_commercial_cost_events_tenant_id"),
|
||||
UniqueConstraint(
|
||||
"tenant_id",
|
||||
"source_system",
|
||||
"idempotency_key",
|
||||
name="uq_commercial_cost_events_source_request",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id"],
|
||||
["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
|
||||
name="fk_commercial_cost_events_tenant_subscription",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id", "usage_event_id"],
|
||||
[
|
||||
"usage_meter_events.tenant_id",
|
||||
"usage_meter_events.subscription_id",
|
||||
"usage_meter_events.id",
|
||||
],
|
||||
name="fk_commercial_cost_events_tenant_usage",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id", "billing_period_id"],
|
||||
[
|
||||
"commercial_billing_periods.tenant_id",
|
||||
"commercial_billing_periods.subscription_id",
|
||||
"commercial_billing_periods.id",
|
||||
],
|
||||
name="fk_commercial_cost_events_tenant_billing_period",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["tenant_id", "reversal_of_cost_event_id"],
|
||||
["commercial_cost_events.tenant_id", "commercial_cost_events.id"],
|
||||
name="fk_commercial_cost_events_tenant_reversal",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
CheckConstraint(
|
||||
"event_type IN ('incurred', 'credit', 'adjustment', 'reversal')",
|
||||
name="ck_commercial_cost_events_type",
|
||||
),
|
||||
CheckConstraint(
|
||||
"cost_category IN ('ai_inference', 'ocr', 'storage', 'connector', "
|
||||
"'support', 'implementation', 'infrastructure', 'payment', 'other')",
|
||||
name="ck_commercial_cost_events_category",
|
||||
),
|
||||
CheckConstraint(
|
||||
"quantity > 0 AND unit_cost >= 0 AND fx_rate > 0",
|
||||
name="ck_commercial_cost_events_values",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(event_type = 'incurred' AND cost_amount >= 0 AND reporting_amount >= 0) OR "
|
||||
"(event_type = 'credit' AND cost_amount <= 0 AND reporting_amount <= 0) OR "
|
||||
"(event_type IN ('adjustment', 'reversal') AND cost_amount <> 0 "
|
||||
"AND reporting_amount <> 0)",
|
||||
name="ck_commercial_cost_events_amount_direction",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(event_type = 'reversal' AND reversal_of_cost_event_id IS NOT NULL) OR "
|
||||
"(event_type != 'reversal' AND reversal_of_cost_event_id IS NULL)",
|
||||
name="ck_commercial_cost_events_reversal",
|
||||
),
|
||||
CheckConstraint(
|
||||
"usage_event_id IS NULL OR subscription_id IS NOT NULL",
|
||||
name="ck_commercial_cost_events_usage_pair",
|
||||
),
|
||||
CheckConstraint(
|
||||
"(subscription_id IS NULL AND billing_period_id IS NULL) OR "
|
||||
"(subscription_id IS NOT NULL AND billing_period_id IS NOT NULL)",
|
||||
name="ck_commercial_cost_events_billing_period_pair",
|
||||
),
|
||||
CheckConstraint(
|
||||
"length(trim(unit)) > 0 AND length(trim(source_system)) > 0 "
|
||||
"AND length(trim(idempotency_key)) > 0 "
|
||||
"AND length(trim(request_fingerprint)) > 0 "
|
||||
"AND length(trim(original_currency)) = 3 "
|
||||
"AND length(trim(reporting_currency)) = 3",
|
||||
name="ck_commercial_cost_events_keys",
|
||||
),
|
||||
Index(
|
||||
"ix_commercial_cost_events_tenant_period",
|
||||
"tenant_id",
|
||||
"occurred_at",
|
||||
"cost_category",
|
||||
),
|
||||
Index(
|
||||
"ix_commercial_cost_events_subscription_period",
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"billing_period_id",
|
||||
"occurred_at",
|
||||
),
|
||||
Index(
|
||||
"ix_commercial_cost_events_allocation",
|
||||
"tenant_id",
|
||||
"allocation_key",
|
||||
"occurred_at",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_new_id)
|
||||
tenant_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
subscription_id: Mapped[str | None] = mapped_column(String(36))
|
||||
billing_period_id: Mapped[str | None] = mapped_column(String(36))
|
||||
usage_event_id: Mapped[str | None] = mapped_column(String(36))
|
||||
event_type: Mapped[str] = mapped_column(String(16), nullable=False)
|
||||
cost_category: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||
quantity: Mapped[Decimal] = mapped_column(Numeric(20, 6), nullable=False)
|
||||
unit: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||
unit_cost: Mapped[Decimal] = mapped_column(Numeric(20, 8), nullable=False)
|
||||
cost_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
|
||||
original_currency: Mapped[str] = mapped_column(String(3), nullable=False)
|
||||
reporting_amount: Mapped[Decimal] = mapped_column(Numeric(20, 4), nullable=False)
|
||||
reporting_currency: Mapped[str] = mapped_column(String(3), nullable=False)
|
||||
fx_rate: Mapped[Decimal] = mapped_column(Numeric(20, 8), nullable=False)
|
||||
provider: Mapped[str | None] = mapped_column(String(120))
|
||||
sku: Mapped[str | None] = mapped_column(String(120))
|
||||
model_name: Mapped[str | None] = mapped_column(String(120))
|
||||
allocation_key: Mapped[str] = mapped_column(String(160), nullable=False)
|
||||
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
source_system: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False)
|
||||
request_fingerprint: Mapped[str] = mapped_column(String(80), nullable=False)
|
||||
reversal_of_cost_event_id: Mapped[str | None] = mapped_column(String(36))
|
||||
correlation_id: Mapped[str | None] = mapped_column(String(120))
|
||||
trace_id: Mapped[str | None] = mapped_column(String(120))
|
||||
metadata_json: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict)
|
||||
recorded_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"CommercialCostEvent",
|
||||
"CommercialEntitlement",
|
||||
"TenantCommercialPlan",
|
||||
"TenantSubscription",
|
||||
"UsageMeterEvent",
|
||||
]
|
||||
Reference in New Issue
Block a user