"""Phase 13 internal accounting model. Revision ID: 20260822_internal_accounting_model_p13 Revises: 20260822_accounting_ai_p12 """ from alembic import op import sqlalchemy as sa revision = "20260822_internal_accounting_model_p13" down_revision = "20260822_accounting_ai_p12" branch_labels = None depends_on = None def upgrade(): op.create_table( "accounting_internal_models", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("tenant_id", sa.Integer(), sa.ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False), sa.Column("version_label", sa.String(80), nullable=False), sa.Column("algorithm", sa.String(80), nullable=False, server_default="multinomial_nb_v1"), sa.Column("status", sa.String(30), nullable=False, server_default="shadow"), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("training_examples", sa.Integer(), nullable=False, server_default="0"), sa.Column("validation_examples", sa.Integer(), nullable=False, server_default="0"), sa.Column("class_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("vocabulary_size", sa.Integer(), nullable=False, server_default="0"), sa.Column("validation_accuracy", sa.Float(), nullable=False, server_default="0"), sa.Column("validation_macro_recall", sa.Float(), nullable=False, server_default="0"), sa.Column("validation_top2_accuracy", sa.Float(), nullable=False, server_default="0"), sa.Column("model_json", sa.Text(), nullable=False), sa.Column("metrics_json", sa.Text(), nullable=True), sa.Column("training_summary_json", sa.Text(), nullable=True), sa.Column("trained_by_user_id", sa.Integer(), sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True), sa.Column("trained_at_utc", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) for name in ("tenant_id", "version_label", "status", "is_active", "trained_at_utc"): op.create_index(f"ix_accounting_internal_models_{name}", "accounting_internal_models", [name]) op.create_table( "accounting_internal_predictions", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("tenant_id", sa.Integer(), sa.ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False), sa.Column("client_id", sa.Integer(), sa.ForeignKey("clients.id", ondelete="CASCADE"), nullable=False), sa.Column("model_id", sa.Integer(), sa.ForeignKey("accounting_internal_models.id", ondelete="CASCADE"), nullable=False), sa.Column("source_type", sa.String(30), nullable=False), sa.Column("source_record_id", sa.Integer(), nullable=False), sa.Column("source_fingerprint", sa.String(80), nullable=False, server_default=""), sa.Column("predicted_nature_id", sa.Integer(), sa.ForeignKey("accounting_natures.id", ondelete="SET NULL"), nullable=True), sa.Column("predicted_probability", sa.Float(), nullable=False, server_default="0"), sa.Column("top2_json", sa.Text(), nullable=True), sa.Column("explanation_json", sa.Text(), nullable=True), sa.Column("shadow_mode", sa.Boolean(), nullable=False, server_default=sa.true()), sa.Column("applied_to_source", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("final_nature_id", sa.Integer(), sa.ForeignKey("accounting_natures.id", ondelete="SET NULL"), nullable=True), sa.Column("prediction_correct", sa.Boolean(), nullable=True), sa.Column("reviewed_by_user_id", sa.Integer(), sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True), sa.Column("reviewed_at_utc", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at_utc", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), ) for name in ( "tenant_id", "client_id", "model_id", "source_type", "source_record_id", "source_fingerprint", "predicted_nature_id", "shadow_mode", "applied_to_source", "final_nature_id", "prediction_correct", "created_at_utc", ): op.create_index( f"ix_accounting_internal_predictions_{name}", "accounting_internal_predictions", [name], ) def downgrade(): op.drop_table("accounting_internal_predictions") op.drop_table("accounting_internal_models")