"""cmdb_cost_facts 折扣前(gross / 原价 / list price)成本列。 Revision ID: 260 Revises: 259 Create Date: 2026-06-06 把 AI 大盘的折扣前轴扩展到云三桶(Foundry/Bedrock/Vertex)。 ``cmdb_cost_facts`` 一直只存折后净额(``amount_usd``);新增**折扣前**: * ``amount_usd_gross`` / ``amount_native_gross`` —— 与净额成对,可空。 None = 该 provider 当前不写原价。本 PR 各 provider 落地状态: - **AWS**:✅ 已接 —— CUR ``pricing/publicOnDemandCost``(BQ 列已存在; 重跑 ``cmdb.cost_facts_sync`` 即回填历史)。 - **GCP**:❌ 未接 —— 列就位但 gcp_cloud.py 暂不写 gross。净值= ``cost+Σcredits`` 是全平台口径变更需 finance 验证,拆 follow-up task; 在那之前 GCP 行 gross 恒 NULL。 - **Azure**:❌ 未接 —— 零售价需 Price Sheet API、历史聚合行无法回填, 拆 follow-up task;在那之前 Azure 行 gross 恒 NULL。 不做数据回填:净额是冻结的逐行账单,gross 必须来自源(AWS 重跑;GCP/Azure 待各自 follow-up),不能从净额臆造(见 [[feedback_no_speculative_backfill]] 精神)。 幂等:``_column_exists`` 守卫。Downgrade 删列。 """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "260" down_revision = "259" branch_labels = None depends_on = None _TABLE = "cmdb_cost_facts" def _column_exists(table: str, column: str) -> bool: conn = op.get_bind() result = conn.execute( sa.text( "SELECT COUNT(*) FROM information_schema.columns " "WHERE table_schema = DATABASE() " "AND table_name = :t AND column_name = :c" ), {"t": table, "c": column}, ) return (result.scalar() or 0) > 0 def upgrade() -> None: if not _column_exists(_TABLE, "amount_usd_gross"): op.add_column( _TABLE, sa.Column("amount_usd_gross", sa.Numeric(14, 6), nullable=True), ) if not _column_exists(_TABLE, "amount_native_gross"): op.add_column( _TABLE, sa.Column("amount_native_gross", sa.Numeric(14, 6), nullable=True), ) def downgrade() -> None: if _column_exists(_TABLE, "amount_native_gross"): op.drop_column(_TABLE, "amount_native_gross") if _column_exists(_TABLE, "amount_usd_gross"): op.drop_column(_TABLE, "amount_usd_gross")