"""v0.1.0 baseline - 19 tables

Revision ID: 03d8dcc2d72a
Revises: 
Create Date: 2026-03-15 15:21:25.537922

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = '03d8dcc2d72a'
down_revision: Union[str, Sequence[str], None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    """Upgrade schema."""
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index('idx_follow_cust', table_name='crm_follow_up_logs', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_follow_sales', table_name='crm_follow_up_logs', postgresql_where='(is_deleted = false)')
    op.drop_table('crm_follow_up_logs')
    op.alter_column('ai_chat_sessions', 'msg_type',
               existing_type=sa.VARCHAR(length=20),
               nullable=False,
               existing_server_default=sa.text("'text'::character varying"))
    op.alter_column('ai_chat_sessions', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.drop_index('idx_chat_user_time', table_name='ai_chat_sessions')
    op.drop_table_comment(
        'ai_chat_sessions',
        existing_comment='AI 悬浮球对话历史（按用户分区）',
        schema=None
    )
    op.alter_column('ai_report_drafts', 'status',
               existing_type=sa.VARCHAR(length=20),
               nullable=False,
               existing_server_default=sa.text("'draft'::character varying"))
    op.alter_column('ai_report_drafts', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('ai_report_drafts', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('ai_report_drafts', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_report_author', table_name='ai_report_drafts', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_report_period', table_name='ai_report_drafts')
    op.drop_table_comment(
        'ai_report_drafts',
        existing_comment='AI 生成的报告草稿（周报/月报）',
        schema=None
    )
    op.alter_column('crm_contacts', 'customer_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='关联的客户 UUID',
               existing_nullable=False)
    op.alter_column('crm_contacts', 'name',
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment='联系人姓名',
               existing_nullable=False)
    op.alter_column('crm_contacts', 'phone',
               existing_type=sa.VARCHAR(length=30),
               comment=None,
               existing_comment='联系人电话',
               existing_nullable=True)
    op.alter_column('crm_contacts', 'title',
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment='职位/头衔',
               existing_nullable=True)
    op.alter_column('crm_contacts', 'ai_buyer_persona',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               comment=None,
               existing_comment='联系人个体画像 JSONB：role / kpi / preference',
               existing_nullable=True,
               existing_server_default=sa.text("'{}'::jsonb"))
    op.alter_column('crm_contacts', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.alter_column('crm_contacts', 'created_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               nullable=False,
               existing_server_default=sa.text('now()'))
    op.alter_column('crm_contacts', 'updated_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=sa.DateTime(),
               nullable=False,
               existing_server_default=sa.text('now()'))
    op.drop_index('idx_contacts_customer', table_name='crm_contacts')
    op.drop_index('idx_contacts_not_deleted', table_name='crm_contacts', postgresql_where='(is_deleted = false)')
    op.drop_constraint('crm_contacts_customer_id_fkey', 'crm_contacts', type_='foreignkey')
    op.create_foreign_key(None, 'crm_contacts', 'crm_customers', ['customer_id'], ['id'])
    op.drop_table_comment(
        'crm_contacts',
        existing_comment='客户联系人子表 (V5.0)',
        schema=None
    )
    op.alter_column('crm_customers', 'id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='客户主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.alter_column('crm_customers', 'name',
               existing_type=sa.VARCHAR(length=200),
               comment=None,
               existing_comment='客户/公司名称',
               existing_nullable=False)
    op.alter_column('crm_customers', 'level',
               existing_type=sa.CHAR(length=1),
               type_=sa.String(length=1),
               nullable=False,
               comment=None,
               existing_comment='客户等级 A/B/C',
               existing_server_default=sa.text("'C'::bpchar"))
    op.alter_column('crm_customers', 'ai_score',
               existing_type=sa.NUMERIC(precision=5, scale=2),
               nullable=False,
               comment=None,
               existing_comment='AI客情健康度评分 0~100',
               existing_server_default=sa.text('0'))
    op.alter_column('crm_customers', 'ai_persona',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               comment=None,
               existing_comment='AI 提炼的客户画像（痛点/偏好/购买习惯等）',
               existing_nullable=True,
               existing_server_default=sa.text("'{}'::jsonb"))
    op.alter_column('crm_customers', 'owner_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='负责销售ID',
               existing_nullable=True)
    op.alter_column('crm_customers', 'status',
               existing_type=sa.SMALLINT(),
               nullable=False,
               existing_server_default=sa.text('1'))
    op.alter_column('crm_customers', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('crm_customers', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('crm_customers', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_cust_level', table_name='crm_customers', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_cust_name', table_name='crm_customers', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_cust_name_trgm', table_name='crm_customers', postgresql_using='gin')
    op.drop_index('idx_cust_owner', table_name='crm_customers', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'crm_customers',
        existing_comment='客户主表',
        schema=None
    )
    op.alter_column('erp_inventory_flows', 'sku_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='关联SKU ID',
               existing_nullable=False)
    op.alter_column('erp_inventory_flows', 'change_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment=None,
               existing_comment='变动数量：正=入库 负=出库',
               existing_nullable=False)
    op.alter_column('erp_inventory_flows', 'reason',
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment='变动原因: purchase/shipment/loss/adjust',
               existing_nullable=False)
    op.alter_column('erp_inventory_flows', 'operator_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='操作人ID',
               existing_nullable=True)
    op.alter_column('erp_inventory_flows', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_inventory_flows', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_inventory_flows', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_invflow_operator', table_name='erp_inventory_flows', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_invflow_sku', table_name='erp_inventory_flows', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_invflow_time', table_name='erp_inventory_flows', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'erp_inventory_flows',
        existing_comment='出入库流水账表',
        schema=None
    )
    op.alter_column('erp_order_items', 'order_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='归属订单ID',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'sku_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='关联SKU ID',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment=None,
               existing_comment='下单数量',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'unit_price',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment=None,
               existing_comment='成交单价（可能为客户专属价）',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'sub_total',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment=None,
               existing_comment='小计金额 = qty * unit_price',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'shipped_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               nullable=False,
               comment=None,
               existing_comment='已发货累计数量，用于分批发货进度追踪',
               existing_server_default=sa.text('0'))
    op.alter_column('erp_order_items', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_order_items', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_order_items', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_oitem_order', table_name='erp_order_items', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_oitem_sku', table_name='erp_order_items', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'erp_order_items',
        existing_comment='订单明细行表',
        schema=None
    )
    op.alter_column('erp_orders', 'order_no',
               existing_type=sa.VARCHAR(length=30),
               comment=None,
               existing_comment='业务订单号',
               existing_nullable=False)
    op.alter_column('erp_orders', 'customer_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='下单客户ID',
               existing_nullable=False)
    op.alter_column('erp_orders', 'salesperson_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='负责销售ID',
               existing_nullable=True)
    op.alter_column('erp_orders', 'total_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment=None,
               existing_comment='订单总金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_orders', 'shipping_state',
               existing_type=sa.VARCHAR(length=20),
               comment=None,
               existing_comment='发货状态: pending/partial/shipped',
               existing_nullable=False,
               existing_server_default=sa.text("'pending'::character varying"))
    op.alter_column('erp_orders', 'payment_state',
               existing_type=sa.VARCHAR(length=20),
               comment=None,
               existing_comment='付款状态: unpaid/partial/cleared',
               existing_nullable=False,
               existing_server_default=sa.text("'unpaid'::character varying"))
    op.alter_column('erp_orders', 'paid_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               nullable=False,
               comment=None,
               existing_comment='已付金额',
               existing_server_default=sa.text('0'))
    op.alter_column('erp_orders', 'order_date',
               existing_type=sa.DATE(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_DATE'))
    op.alter_column('erp_orders', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_orders', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_orders', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_order_cust', table_name='erp_orders', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_order_date', table_name='erp_orders', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_order_no', table_name='erp_orders', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_order_sales', table_name='erp_orders', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'erp_orders',
        existing_comment='订单主表',
        schema=None
    )
    op.alter_column('erp_product_categories', 'parent_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='父级分类ID，NULL为顶级分类',
               existing_nullable=True)
    op.alter_column('erp_product_categories', 'sort_order',
               existing_type=sa.INTEGER(),
               nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_categories', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_categories', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_categories', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_pcat_parent', table_name='erp_product_categories', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'erp_product_categories',
        existing_comment='产品分类树表（左树结构支撑）',
        schema=None
    )
    op.alter_column('erp_product_skus', 'sku_code',
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment='SKU编号，全局唯一',
               existing_nullable=False)
    op.alter_column('erp_product_skus', 'spec',
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment='包装规格，如 200L/桶、18L/桶',
               existing_nullable=True)
    op.alter_column('erp_product_skus', 'standard_price',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment=None,
               existing_comment='标准售价',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_skus', 'stock_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment=None,
               existing_comment='当前库存数量',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_skus', 'warning_threshold',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               nullable=False,
               comment=None,
               existing_comment='库存预警阈值，低于此值触发预警',
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_skus', 'unit',
               existing_type=sa.VARCHAR(length=20),
               nullable=False,
               existing_server_default=sa.text("'桶'::character varying"))
    op.alter_column('erp_product_skus', 'status',
               existing_type=sa.SMALLINT(),
               nullable=False,
               existing_server_default=sa.text('1'))
    op.alter_column('erp_product_skus', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_skus', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_skus', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_sku_category', table_name='erp_product_skus', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_sku_code', table_name='erp_product_skus', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'erp_product_skus',
        existing_comment='产品SKU主档表',
        schema=None
    )
    op.alter_column('erp_shipping_items', 'shipping_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='归属发货单ID',
               existing_nullable=False)
    op.alter_column('erp_shipping_items', 'order_item_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='关联订单明细行ID（用于回写已发货量）',
               existing_nullable=False)
    op.alter_column('erp_shipping_items', 'sku_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='关联SKU ID',
               existing_nullable=False)
    op.alter_column('erp_shipping_items', 'shipped_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment=None,
               existing_comment='本次实际发货数量',
               existing_nullable=False)
    op.alter_column('erp_shipping_items', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_items', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_items', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_sitem_oitem', table_name='erp_shipping_items', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_sitem_shipping', table_name='erp_shipping_items', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'erp_shipping_items',
        existing_comment='发货明细行表（承接分批发货扣减）',
        schema=None
    )
    op.alter_column('erp_shipping_records', 'shipping_no',
               existing_type=sa.VARCHAR(length=30),
               comment=None,
               existing_comment='发货单号',
               existing_nullable=False)
    op.alter_column('erp_shipping_records', 'order_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='关联订单ID',
               existing_nullable=False)
    op.alter_column('erp_shipping_records', 'carrier',
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment='承运方',
               existing_nullable=True)
    op.alter_column('erp_shipping_records', 'tracking_no',
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment='物流追踪号',
               existing_nullable=True)
    op.alter_column('erp_shipping_records', 'status',
               existing_type=sa.VARCHAR(length=20),
               comment=None,
               existing_comment='物流状态: transit=运输中 / delivered=已签收',
               existing_nullable=False,
               existing_server_default=sa.text("'transit'::character varying"))
    op.alter_column('erp_shipping_records', 'ship_date',
               existing_type=sa.DATE(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_DATE'))
    op.alter_column('erp_shipping_records', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_records', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_records', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_ship_no', table_name='erp_shipping_records', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_ship_order', table_name='erp_shipping_records', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'erp_shipping_records',
        existing_comment='发货主单表',
        schema=None
    )
    op.alter_column('fin_expense_details', 'expense_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='归属报销单ID',
               existing_nullable=False)
    op.alter_column('fin_expense_details', 'invoice_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='关联发票ID',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'expense_desc',
               existing_type=sa.VARCHAR(length=500),
               comment=None,
               existing_comment='费用说明描述',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'original_type',
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment='原始费用类型: fuel/entertainment/travel/office',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'offset_type',
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment='冲顶类型（二级票据类型转换后的类型）',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment=None,
               existing_comment='本行金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('fin_expense_details', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_details', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_details', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_expd_expense', table_name='fin_expense_details', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_expd_invoice', table_name='fin_expense_details', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'fin_expense_details',
        existing_comment='报销明细行表',
        schema=None
    )
    op.alter_column('fin_expense_records', 'system_no',
               existing_type=sa.VARCHAR(length=30),
               comment=None,
               existing_comment='系统报销单号',
               existing_nullable=False)
    op.alter_column('fin_expense_records', 'applicant_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='申请人ID',
               existing_nullable=False)
    op.alter_column('fin_expense_records', 'total_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment=None,
               existing_comment='报销总金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('fin_expense_records', 'status',
               existing_type=sa.VARCHAR(length=20),
               comment=None,
               existing_comment='报销状态: draft/pending/approved/rejected',
               existing_nullable=False,
               existing_server_default=sa.text("'draft'::character varying"))
    op.alter_column('fin_expense_records', 'approved_by',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='审批人ID',
               existing_nullable=True)
    op.alter_column('fin_expense_records', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_records', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_records', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_exp_applicant', table_name='fin_expense_records', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_exp_no', table_name='fin_expense_records', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_exp_status', table_name='fin_expense_records', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'fin_expense_records',
        existing_comment='报销单主表',
        schema=None
    )
    op.alter_column('fin_invoice_pool', 'uploader_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='上传人ID',
               existing_nullable=True)
    op.alter_column('fin_invoice_pool', 'file_url',
               existing_type=sa.VARCHAR(length=500),
               comment=None,
               existing_comment='原始票据文件URL',
               existing_nullable=True)
    op.alter_column('fin_invoice_pool', 'merchant_name',
               existing_type=sa.VARCHAR(length=200),
               comment=None,
               existing_comment='商户/开票方名称',
               existing_nullable=True)
    op.alter_column('fin_invoice_pool', 'amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment=None,
               existing_comment='票面金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('fin_invoice_pool', 'type',
               existing_type=sa.VARCHAR(length=30),
               comment=None,
               existing_comment='票据类型: customer=客户发票 / expense=报销发票',
               existing_nullable=False,
               existing_server_default=sa.text("'expense'::character varying"))
    op.alter_column('fin_invoice_pool', 'ai_extracted_data',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               nullable=False,
               comment=None,
               existing_comment='AI/OCR提取的结构化数据 (JSONB)',
               existing_server_default=sa.text("'{}'::jsonb"))
    op.alter_column('fin_invoice_pool', 'is_used',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               comment=None,
               existing_comment='是否已被报销单引用',
               existing_server_default=sa.text('false'))
    op.alter_column('fin_invoice_pool', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_invoice_pool', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_invoice_pool', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               comment=None,
               existing_comment='软删除标识（作废，防物理篡改）',
               existing_server_default=sa.text('false'))
    op.drop_index('idx_inv_date', table_name='fin_invoice_pool', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_inv_type', table_name='fin_invoice_pool', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_inv_uploader', table_name='fin_invoice_pool', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'fin_invoice_pool',
        existing_comment='统一发票池表（上传即OCR提取）',
        schema=None
    )
    op.alter_column('finance_sales_invoices', 'issuer',
               existing_type=sa.VARCHAR(length=200),
               comment=None,
               existing_comment='开票方/我方主体名称',
               existing_nullable=False)
    op.alter_column('finance_sales_invoices', 'receiver_customer_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='受票方，关联 crm_customers',
               existing_nullable=False)
    op.alter_column('finance_sales_invoices', 'invoice_number',
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment='发票号码（全局唯一）',
               existing_nullable=False)
    op.alter_column('finance_sales_invoices', 'amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment=None,
               existing_comment='票面金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('finance_sales_invoices', 'billing_date',
               existing_type=sa.DATE(),
               comment=None,
               existing_comment='开票日期',
               existing_nullable=False)
    op.alter_column('finance_sales_invoices', 'payment_status',
               existing_type=sa.VARCHAR(length=20),
               comment=None,
               existing_comment='回款状态: 未回款/部分回款/已结清',
               existing_nullable=False,
               existing_server_default=sa.text("'未回款'::character varying"))
    op.alter_column('finance_sales_invoices', 'payment_date',
               existing_type=sa.DATE(),
               comment=None,
               existing_comment='回款日期',
               existing_nullable=True)
    op.alter_column('finance_sales_invoices', 'payment_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               nullable=False,
               comment=None,
               existing_comment='已回款金额（用于部分回款场景）',
               existing_server_default=sa.text('0'))
    op.alter_column('finance_sales_invoices', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('finance_sales_invoices', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('finance_sales_invoices', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_fsi_billing_date', table_name='finance_sales_invoices', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_fsi_number', table_name='finance_sales_invoices', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_fsi_payment_status', table_name='finance_sales_invoices', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_fsi_receiver', table_name='finance_sales_invoices', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'finance_sales_invoices',
        existing_comment='销项发票表（AR 应收账款核心）',
        schema=None
    )
    op.alter_column('sales_logs', 'log_date',
               existing_type=sa.DATE(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_DATE'))
    op.alter_column('sales_logs', 'contact_ids',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               comment=None,
               existing_comment='本次沟通涉及的联系人 UUID 列表 (JSONB Array)',
               existing_nullable=True,
               existing_server_default=sa.text("'[]'::jsonb"))
    op.alter_column('sales_logs', 'ai_processed',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.alter_column('sales_logs', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sales_logs', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sales_logs', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_slog_cust', table_name='sales_logs', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_slog_date', table_name='sales_logs', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_slog_sales', table_name='sales_logs', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'sales_logs',
        existing_comment='销售日志表（一源多用数据源）',
        schema=None
    )
    op.alter_column('sys_departments', 'id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='部门主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.alter_column('sys_departments', 'parent_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='父级部门ID，NULL表示顶级',
               existing_nullable=True)
    op.alter_column('sys_departments', 'name',
               existing_type=sa.VARCHAR(length=100),
               comment=None,
               existing_comment='部门名称',
               existing_nullable=False)
    op.alter_column('sys_departments', 'sort_order',
               existing_type=sa.INTEGER(),
               type_=sa.SmallInteger(),
               nullable=False,
               comment=None,
               existing_comment='排序序号',
               existing_server_default=sa.text('0'))
    op.alter_column('sys_departments', 'status',
               existing_type=sa.SMALLINT(),
               nullable=False,
               comment=None,
               existing_comment='状态 1:启用 0:停用',
               existing_server_default=sa.text('1'))
    op.alter_column('sys_departments', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_departments', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_departments', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_dept_parent', table_name='sys_departments', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'sys_departments',
        existing_comment='部门树表',
        schema=None
    )
    op.alter_column('sys_roles', 'id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='角色主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.alter_column('sys_roles', 'role_name',
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment='角色名称',
               existing_nullable=False)
    op.alter_column('sys_roles', 'data_scope',
               existing_type=sa.VARCHAR(length=20),
               comment=None,
               existing_comment='数据权限范围: all=全部 / dept_and_sub=本部门及下属 / self=仅本人',
               existing_nullable=False,
               existing_server_default=sa.text("'self'::character varying"))
    op.alter_column('sys_roles', 'menu_keys',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               nullable=False,
               comment=None,
               existing_comment='拥有的菜单/按钮权限键列表 (JSONB数组)',
               existing_server_default=sa.text("'[]'::jsonb"))
    op.alter_column('sys_roles', 'status',
               existing_type=sa.SMALLINT(),
               nullable=False,
               existing_server_default=sa.text('1'))
    op.alter_column('sys_roles', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_roles', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_roles', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_table_comment(
        'sys_roles',
        existing_comment='角色表',
        schema=None
    )
    op.alter_column('sys_users', 'id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='用户主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.alter_column('sys_users', 'dept_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='所属部门ID',
               existing_nullable=True)
    op.alter_column('sys_users', 'role_id',
               existing_type=sa.UUID(),
               comment=None,
               existing_comment='所属角色ID',
               existing_nullable=True)
    op.alter_column('sys_users', 'username',
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment='登录账号',
               existing_nullable=False)
    op.alter_column('sys_users', 'password_hash',
               existing_type=sa.VARCHAR(length=255),
               comment=None,
               existing_comment='密码哈希值',
               existing_nullable=False)
    op.alter_column('sys_users', 'real_name',
               existing_type=sa.VARCHAR(length=50),
               comment=None,
               existing_comment='真实姓名',
               existing_nullable=True)
    op.alter_column('sys_users', 'status',
               existing_type=sa.SMALLINT(),
               nullable=False,
               comment=None,
               existing_comment='状态 1:在职 0:离职',
               existing_server_default=sa.text('1'))
    op.alter_column('sys_users', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_users', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=False,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_users', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=False,
               existing_server_default=sa.text('false'))
    op.drop_index('idx_user_dept', table_name='sys_users', postgresql_where='(is_deleted = false)')
    op.drop_index('idx_user_role', table_name='sys_users', postgresql_where='(is_deleted = false)')
    op.drop_table_comment(
        'sys_users',
        existing_comment='员工/账号表',
        schema=None
    )
    # ### end Alembic commands ###


def downgrade() -> None:
    """Downgrade schema."""
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table_comment(
        'sys_users',
        '员工/账号表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_user_role', 'sys_users', ['role_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_user_dept', 'sys_users', ['dept_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('sys_users', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('sys_users', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_users', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_users', 'status',
               existing_type=sa.SMALLINT(),
               nullable=True,
               comment='状态 1:在职 0:离职',
               existing_server_default=sa.text('1'))
    op.alter_column('sys_users', 'real_name',
               existing_type=sa.VARCHAR(length=50),
               comment='真实姓名',
               existing_nullable=True)
    op.alter_column('sys_users', 'password_hash',
               existing_type=sa.VARCHAR(length=255),
               comment='密码哈希值',
               existing_nullable=False)
    op.alter_column('sys_users', 'username',
               existing_type=sa.VARCHAR(length=50),
               comment='登录账号',
               existing_nullable=False)
    op.alter_column('sys_users', 'role_id',
               existing_type=sa.UUID(),
               comment='所属角色ID',
               existing_nullable=True)
    op.alter_column('sys_users', 'dept_id',
               existing_type=sa.UUID(),
               comment='所属部门ID',
               existing_nullable=True)
    op.alter_column('sys_users', 'id',
               existing_type=sa.UUID(),
               comment='用户主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.create_table_comment(
        'sys_roles',
        '角色表',
        existing_comment=None,
        schema=None
    )
    op.alter_column('sys_roles', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('sys_roles', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_roles', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_roles', 'status',
               existing_type=sa.SMALLINT(),
               nullable=True,
               existing_server_default=sa.text('1'))
    op.alter_column('sys_roles', 'menu_keys',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               nullable=True,
               comment='拥有的菜单/按钮权限键列表 (JSONB数组)',
               existing_server_default=sa.text("'[]'::jsonb"))
    op.alter_column('sys_roles', 'data_scope',
               existing_type=sa.VARCHAR(length=20),
               comment='数据权限范围: all=全部 / dept_and_sub=本部门及下属 / self=仅本人',
               existing_nullable=False,
               existing_server_default=sa.text("'self'::character varying"))
    op.alter_column('sys_roles', 'role_name',
               existing_type=sa.VARCHAR(length=50),
               comment='角色名称',
               existing_nullable=False)
    op.alter_column('sys_roles', 'id',
               existing_type=sa.UUID(),
               comment='角色主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.create_table_comment(
        'sys_departments',
        '部门树表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_dept_parent', 'sys_departments', ['parent_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('sys_departments', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('sys_departments', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_departments', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sys_departments', 'status',
               existing_type=sa.SMALLINT(),
               nullable=True,
               comment='状态 1:启用 0:停用',
               existing_server_default=sa.text('1'))
    op.alter_column('sys_departments', 'sort_order',
               existing_type=sa.SmallInteger(),
               type_=sa.INTEGER(),
               nullable=True,
               comment='排序序号',
               existing_server_default=sa.text('0'))
    op.alter_column('sys_departments', 'name',
               existing_type=sa.VARCHAR(length=100),
               comment='部门名称',
               existing_nullable=False)
    op.alter_column('sys_departments', 'parent_id',
               existing_type=sa.UUID(),
               comment='父级部门ID，NULL表示顶级',
               existing_nullable=True)
    op.alter_column('sys_departments', 'id',
               existing_type=sa.UUID(),
               comment='部门主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.create_table_comment(
        'sales_logs',
        '销售日志表（一源多用数据源）',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_slog_sales', 'sales_logs', ['salesperson_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_slog_date', 'sales_logs', ['log_date'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_slog_cust', 'sales_logs', ['customer_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('sales_logs', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('sales_logs', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sales_logs', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('sales_logs', 'ai_processed',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('sales_logs', 'contact_ids',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               comment='本次沟通涉及的联系人 UUID 列表 (JSONB Array)',
               existing_nullable=True,
               existing_server_default=sa.text("'[]'::jsonb"))
    op.alter_column('sales_logs', 'log_date',
               existing_type=sa.DATE(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_DATE'))
    op.create_table_comment(
        'finance_sales_invoices',
        '销项发票表（AR 应收账款核心）',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_fsi_receiver', 'finance_sales_invoices', ['receiver_customer_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_fsi_payment_status', 'finance_sales_invoices', ['payment_status'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_fsi_number', 'finance_sales_invoices', ['invoice_number'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_fsi_billing_date', 'finance_sales_invoices', ['billing_date'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('finance_sales_invoices', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('finance_sales_invoices', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('finance_sales_invoices', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('finance_sales_invoices', 'payment_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               nullable=True,
               comment='已回款金额（用于部分回款场景）',
               existing_server_default=sa.text('0'))
    op.alter_column('finance_sales_invoices', 'payment_date',
               existing_type=sa.DATE(),
               comment='回款日期',
               existing_nullable=True)
    op.alter_column('finance_sales_invoices', 'payment_status',
               existing_type=sa.VARCHAR(length=20),
               comment='回款状态: 未回款/部分回款/已结清',
               existing_nullable=False,
               existing_server_default=sa.text("'未回款'::character varying"))
    op.alter_column('finance_sales_invoices', 'billing_date',
               existing_type=sa.DATE(),
               comment='开票日期',
               existing_nullable=False)
    op.alter_column('finance_sales_invoices', 'amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment='票面金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('finance_sales_invoices', 'invoice_number',
               existing_type=sa.VARCHAR(length=100),
               comment='发票号码（全局唯一）',
               existing_nullable=False)
    op.alter_column('finance_sales_invoices', 'receiver_customer_id',
               existing_type=sa.UUID(),
               comment='受票方，关联 crm_customers',
               existing_nullable=False)
    op.alter_column('finance_sales_invoices', 'issuer',
               existing_type=sa.VARCHAR(length=200),
               comment='开票方/我方主体名称',
               existing_nullable=False)
    op.create_table_comment(
        'fin_invoice_pool',
        '统一发票池表（上传即OCR提取）',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_inv_uploader', 'fin_invoice_pool', ['uploader_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_inv_type', 'fin_invoice_pool', ['type'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_inv_date', 'fin_invoice_pool', ['invoice_date'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('fin_invoice_pool', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               comment='软删除标识（作废，防物理篡改）',
               existing_server_default=sa.text('false'))
    op.alter_column('fin_invoice_pool', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_invoice_pool', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_invoice_pool', 'is_used',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               comment='是否已被报销单引用',
               existing_server_default=sa.text('false'))
    op.alter_column('fin_invoice_pool', 'ai_extracted_data',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               nullable=True,
               comment='AI/OCR提取的结构化数据 (JSONB)',
               existing_server_default=sa.text("'{}'::jsonb"))
    op.alter_column('fin_invoice_pool', 'type',
               existing_type=sa.VARCHAR(length=30),
               comment='票据类型: customer=客户发票 / expense=报销发票',
               existing_nullable=False,
               existing_server_default=sa.text("'expense'::character varying"))
    op.alter_column('fin_invoice_pool', 'amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment='票面金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('fin_invoice_pool', 'merchant_name',
               existing_type=sa.VARCHAR(length=200),
               comment='商户/开票方名称',
               existing_nullable=True)
    op.alter_column('fin_invoice_pool', 'file_url',
               existing_type=sa.VARCHAR(length=500),
               comment='原始票据文件URL',
               existing_nullable=True)
    op.alter_column('fin_invoice_pool', 'uploader_id',
               existing_type=sa.UUID(),
               comment='上传人ID',
               existing_nullable=True)
    op.create_table_comment(
        'fin_expense_records',
        '报销单主表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_exp_status', 'fin_expense_records', ['status'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_exp_no', 'fin_expense_records', ['system_no'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_exp_applicant', 'fin_expense_records', ['applicant_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('fin_expense_records', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('fin_expense_records', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_records', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_records', 'approved_by',
               existing_type=sa.UUID(),
               comment='审批人ID',
               existing_nullable=True)
    op.alter_column('fin_expense_records', 'status',
               existing_type=sa.VARCHAR(length=20),
               comment='报销状态: draft/pending/approved/rejected',
               existing_nullable=False,
               existing_server_default=sa.text("'draft'::character varying"))
    op.alter_column('fin_expense_records', 'total_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment='报销总金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('fin_expense_records', 'applicant_id',
               existing_type=sa.UUID(),
               comment='申请人ID',
               existing_nullable=False)
    op.alter_column('fin_expense_records', 'system_no',
               existing_type=sa.VARCHAR(length=30),
               comment='系统报销单号',
               existing_nullable=False)
    op.create_table_comment(
        'fin_expense_details',
        '报销明细行表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_expd_invoice', 'fin_expense_details', ['invoice_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_expd_expense', 'fin_expense_details', ['expense_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('fin_expense_details', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('fin_expense_details', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_details', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('fin_expense_details', 'amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment='本行金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('fin_expense_details', 'offset_type',
               existing_type=sa.VARCHAR(length=50),
               comment='冲顶类型（二级票据类型转换后的类型）',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'original_type',
               existing_type=sa.VARCHAR(length=50),
               comment='原始费用类型: fuel/entertainment/travel/office',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'expense_desc',
               existing_type=sa.VARCHAR(length=500),
               comment='费用说明描述',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'invoice_id',
               existing_type=sa.UUID(),
               comment='关联发票ID',
               existing_nullable=True)
    op.alter_column('fin_expense_details', 'expense_id',
               existing_type=sa.UUID(),
               comment='归属报销单ID',
               existing_nullable=False)
    op.create_table_comment(
        'erp_shipping_records',
        '发货主单表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_ship_order', 'erp_shipping_records', ['order_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_ship_no', 'erp_shipping_records', ['shipping_no'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('erp_shipping_records', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('erp_shipping_records', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_records', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_records', 'ship_date',
               existing_type=sa.DATE(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_DATE'))
    op.alter_column('erp_shipping_records', 'status',
               existing_type=sa.VARCHAR(length=20),
               comment='物流状态: transit=运输中 / delivered=已签收',
               existing_nullable=False,
               existing_server_default=sa.text("'transit'::character varying"))
    op.alter_column('erp_shipping_records', 'tracking_no',
               existing_type=sa.VARCHAR(length=100),
               comment='物流追踪号',
               existing_nullable=True)
    op.alter_column('erp_shipping_records', 'carrier',
               existing_type=sa.VARCHAR(length=100),
               comment='承运方',
               existing_nullable=True)
    op.alter_column('erp_shipping_records', 'order_id',
               existing_type=sa.UUID(),
               comment='关联订单ID',
               existing_nullable=False)
    op.alter_column('erp_shipping_records', 'shipping_no',
               existing_type=sa.VARCHAR(length=30),
               comment='发货单号',
               existing_nullable=False)
    op.create_table_comment(
        'erp_shipping_items',
        '发货明细行表（承接分批发货扣减）',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_sitem_shipping', 'erp_shipping_items', ['shipping_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_sitem_oitem', 'erp_shipping_items', ['order_item_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('erp_shipping_items', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('erp_shipping_items', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_items', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_shipping_items', 'shipped_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment='本次实际发货数量',
               existing_nullable=False)
    op.alter_column('erp_shipping_items', 'sku_id',
               existing_type=sa.UUID(),
               comment='关联SKU ID',
               existing_nullable=False)
    op.alter_column('erp_shipping_items', 'order_item_id',
               existing_type=sa.UUID(),
               comment='关联订单明细行ID（用于回写已发货量）',
               existing_nullable=False)
    op.alter_column('erp_shipping_items', 'shipping_id',
               existing_type=sa.UUID(),
               comment='归属发货单ID',
               existing_nullable=False)
    op.create_table_comment(
        'erp_product_skus',
        '产品SKU主档表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_sku_code', 'erp_product_skus', ['sku_code'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_sku_category', 'erp_product_skus', ['category_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('erp_product_skus', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('erp_product_skus', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_skus', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_skus', 'status',
               existing_type=sa.SMALLINT(),
               nullable=True,
               existing_server_default=sa.text('1'))
    op.alter_column('erp_product_skus', 'unit',
               existing_type=sa.VARCHAR(length=20),
               nullable=True,
               existing_server_default=sa.text("'桶'::character varying"))
    op.alter_column('erp_product_skus', 'warning_threshold',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               nullable=True,
               comment='库存预警阈值，低于此值触发预警',
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_skus', 'stock_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment='当前库存数量',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_skus', 'standard_price',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment='标准售价',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_skus', 'spec',
               existing_type=sa.VARCHAR(length=100),
               comment='包装规格，如 200L/桶、18L/桶',
               existing_nullable=True)
    op.alter_column('erp_product_skus', 'sku_code',
               existing_type=sa.VARCHAR(length=50),
               comment='SKU编号，全局唯一',
               existing_nullable=False)
    op.create_table_comment(
        'erp_product_categories',
        '产品分类树表（左树结构支撑）',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_pcat_parent', 'erp_product_categories', ['parent_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('erp_product_categories', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('erp_product_categories', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_categories', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_product_categories', 'sort_order',
               existing_type=sa.INTEGER(),
               nullable=True,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_product_categories', 'parent_id',
               existing_type=sa.UUID(),
               comment='父级分类ID，NULL为顶级分类',
               existing_nullable=True)
    op.create_table_comment(
        'erp_orders',
        '订单主表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_order_sales', 'erp_orders', ['salesperson_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_order_no', 'erp_orders', ['order_no'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_order_date', 'erp_orders', ['order_date'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_order_cust', 'erp_orders', ['customer_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('erp_orders', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('erp_orders', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_orders', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_orders', 'order_date',
               existing_type=sa.DATE(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_DATE'))
    op.alter_column('erp_orders', 'paid_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               nullable=True,
               comment='已付金额',
               existing_server_default=sa.text('0'))
    op.alter_column('erp_orders', 'payment_state',
               existing_type=sa.VARCHAR(length=20),
               comment='付款状态: unpaid/partial/cleared',
               existing_nullable=False,
               existing_server_default=sa.text("'unpaid'::character varying"))
    op.alter_column('erp_orders', 'shipping_state',
               existing_type=sa.VARCHAR(length=20),
               comment='发货状态: pending/partial/shipped',
               existing_nullable=False,
               existing_server_default=sa.text("'pending'::character varying"))
    op.alter_column('erp_orders', 'total_amount',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment='订单总金额',
               existing_nullable=False,
               existing_server_default=sa.text('0'))
    op.alter_column('erp_orders', 'salesperson_id',
               existing_type=sa.UUID(),
               comment='负责销售ID',
               existing_nullable=True)
    op.alter_column('erp_orders', 'customer_id',
               existing_type=sa.UUID(),
               comment='下单客户ID',
               existing_nullable=False)
    op.alter_column('erp_orders', 'order_no',
               existing_type=sa.VARCHAR(length=30),
               comment='业务订单号',
               existing_nullable=False)
    op.create_table_comment(
        'erp_order_items',
        '订单明细行表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_oitem_sku', 'erp_order_items', ['sku_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_oitem_order', 'erp_order_items', ['order_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('erp_order_items', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('erp_order_items', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_order_items', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_order_items', 'shipped_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               nullable=True,
               comment='已发货累计数量，用于分批发货进度追踪',
               existing_server_default=sa.text('0'))
    op.alter_column('erp_order_items', 'sub_total',
               existing_type=sa.NUMERIC(precision=14, scale=2),
               comment='小计金额 = qty * unit_price',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'unit_price',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment='成交单价（可能为客户专属价）',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment='下单数量',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'sku_id',
               existing_type=sa.UUID(),
               comment='关联SKU ID',
               existing_nullable=False)
    op.alter_column('erp_order_items', 'order_id',
               existing_type=sa.UUID(),
               comment='归属订单ID',
               existing_nullable=False)
    op.create_table_comment(
        'erp_inventory_flows',
        '出入库流水账表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_invflow_time', 'erp_inventory_flows', ['created_at'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_invflow_sku', 'erp_inventory_flows', ['sku_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_invflow_operator', 'erp_inventory_flows', ['operator_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('erp_inventory_flows', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('erp_inventory_flows', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_inventory_flows', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('erp_inventory_flows', 'operator_id',
               existing_type=sa.UUID(),
               comment='操作人ID',
               existing_nullable=True)
    op.alter_column('erp_inventory_flows', 'reason',
               existing_type=sa.VARCHAR(length=50),
               comment='变动原因: purchase/shipment/loss/adjust',
               existing_nullable=False)
    op.alter_column('erp_inventory_flows', 'change_qty',
               existing_type=sa.NUMERIC(precision=12, scale=2),
               comment='变动数量：正=入库 负=出库',
               existing_nullable=False)
    op.alter_column('erp_inventory_flows', 'sku_id',
               existing_type=sa.UUID(),
               comment='关联SKU ID',
               existing_nullable=False)
    op.create_table_comment(
        'crm_customers',
        '客户主表',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_cust_owner', 'crm_customers', ['owner_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_cust_name_trgm', 'crm_customers', ['name'], unique=False, postgresql_using='gin')
    op.create_index('idx_cust_name', 'crm_customers', ['name'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_cust_level', 'crm_customers', ['level'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('crm_customers', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('crm_customers', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('crm_customers', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('crm_customers', 'status',
               existing_type=sa.SMALLINT(),
               nullable=True,
               existing_server_default=sa.text('1'))
    op.alter_column('crm_customers', 'owner_id',
               existing_type=sa.UUID(),
               comment='负责销售ID',
               existing_nullable=True)
    op.alter_column('crm_customers', 'ai_persona',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               comment='AI 提炼的客户画像（痛点/偏好/购买习惯等）',
               existing_nullable=True,
               existing_server_default=sa.text("'{}'::jsonb"))
    op.alter_column('crm_customers', 'ai_score',
               existing_type=sa.NUMERIC(precision=5, scale=2),
               nullable=True,
               comment='AI客情健康度评分 0~100',
               existing_server_default=sa.text('0'))
    op.alter_column('crm_customers', 'level',
               existing_type=sa.String(length=1),
               type_=sa.CHAR(length=1),
               nullable=True,
               comment='客户等级 A/B/C',
               existing_server_default=sa.text("'C'::bpchar"))
    op.alter_column('crm_customers', 'name',
               existing_type=sa.VARCHAR(length=200),
               comment='客户/公司名称',
               existing_nullable=False)
    op.alter_column('crm_customers', 'id',
               existing_type=sa.UUID(),
               comment='客户主键 UUID',
               existing_nullable=False,
               existing_server_default=sa.text('uuid_generate_v4()'))
    op.create_table_comment(
        'crm_contacts',
        '客户联系人子表 (V5.0)',
        existing_comment=None,
        schema=None
    )
    op.drop_constraint(None, 'crm_contacts', type_='foreignkey')
    op.create_foreign_key('crm_contacts_customer_id_fkey', 'crm_contacts', 'crm_customers', ['customer_id'], ['id'], ondelete='CASCADE')
    op.create_index('idx_contacts_not_deleted', 'crm_contacts', ['customer_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_contacts_customer', 'crm_contacts', ['customer_id'], unique=False)
    op.alter_column('crm_contacts', 'updated_at',
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               nullable=True,
               existing_server_default=sa.text('now()'))
    op.alter_column('crm_contacts', 'created_at',
               existing_type=sa.DateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               nullable=True,
               existing_server_default=sa.text('now()'))
    op.alter_column('crm_contacts', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('crm_contacts', 'ai_buyer_persona',
               existing_type=postgresql.JSONB(astext_type=sa.Text()),
               comment='联系人个体画像 JSONB：role / kpi / preference',
               existing_nullable=True,
               existing_server_default=sa.text("'{}'::jsonb"))
    op.alter_column('crm_contacts', 'title',
               existing_type=sa.VARCHAR(length=100),
               comment='职位/头衔',
               existing_nullable=True)
    op.alter_column('crm_contacts', 'phone',
               existing_type=sa.VARCHAR(length=30),
               comment='联系人电话',
               existing_nullable=True)
    op.alter_column('crm_contacts', 'name',
               existing_type=sa.VARCHAR(length=100),
               comment='联系人姓名',
               existing_nullable=False)
    op.alter_column('crm_contacts', 'customer_id',
               existing_type=sa.UUID(),
               comment='关联的客户 UUID',
               existing_nullable=False)
    op.create_table_comment(
        'ai_report_drafts',
        'AI 生成的报告草稿（周报/月报）',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_report_period', 'ai_report_drafts', ['period_start', 'period_end'], unique=False)
    op.create_index('idx_report_author', 'ai_report_drafts', ['author_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.alter_column('ai_report_drafts', 'is_deleted',
               existing_type=sa.BOOLEAN(),
               nullable=True,
               existing_server_default=sa.text('false'))
    op.alter_column('ai_report_drafts', 'updated_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('ai_report_drafts', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('ai_report_drafts', 'status',
               existing_type=sa.VARCHAR(length=20),
               nullable=True,
               existing_server_default=sa.text("'draft'::character varying"))
    op.create_table_comment(
        'ai_chat_sessions',
        'AI 悬浮球对话历史（按用户分区）',
        existing_comment=None,
        schema=None
    )
    op.create_index('idx_chat_user_time', 'ai_chat_sessions', ['user_id', sa.text('created_at DESC')], unique=False)
    op.alter_column('ai_chat_sessions', 'created_at',
               existing_type=postgresql.TIMESTAMP(),
               nullable=True,
               existing_server_default=sa.text('CURRENT_TIMESTAMP'))
    op.alter_column('ai_chat_sessions', 'msg_type',
               existing_type=sa.VARCHAR(length=20),
               nullable=True,
               existing_server_default=sa.text("'text'::character varying"))
    op.create_table('crm_follow_up_logs',
    sa.Column('id', sa.UUID(), server_default=sa.text('uuid_generate_v4()'), autoincrement=False, nullable=False),
    sa.Column('customer_id', sa.UUID(), autoincrement=False, nullable=False, comment='关联客户ID'),
    sa.Column('salesperson_id', sa.UUID(), autoincrement=False, nullable=False, comment='操作销售人员ID'),
    sa.Column('content', sa.TEXT(), autoincrement=False, nullable=False, comment='跟进内容'),
    sa.Column('emotion_type', sa.VARCHAR(length=20), autoincrement=False, nullable=True, comment='情感标记: positive/neutral/negative'),
    sa.Column('next_visit_time', postgresql.TIMESTAMP(), autoincrement=False, nullable=True, comment='计划下次拜访时间'),
    sa.Column('created_at', postgresql.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), autoincrement=False, nullable=True),
    sa.Column('updated_at', postgresql.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), autoincrement=False, nullable=True),
    sa.Column('is_deleted', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=True),
    sa.ForeignKeyConstraint(['customer_id'], ['crm_customers.id'], name='crm_follow_up_logs_customer_id_fkey'),
    sa.ForeignKeyConstraint(['salesperson_id'], ['sys_users.id'], name='crm_follow_up_logs_salesperson_id_fkey'),
    sa.PrimaryKeyConstraint('id', name='crm_follow_up_logs_pkey'),
    comment='客户跟进日志表（AI简报数据源）'
    )
    op.create_index('idx_follow_sales', 'crm_follow_up_logs', ['salesperson_id'], unique=False, postgresql_where='(is_deleted = false)')
    op.create_index('idx_follow_cust', 'crm_follow_up_logs', ['customer_id'], unique=False, postgresql_where='(is_deleted = false)')
    # ### end Alembic commands ###
