"""Phase 15 customer and sales ledger intelligence. Revision ID: 20260822_sales_customer_learning_p15 Revises: 20260822_sales_income_foundation_p14 """ from alembic import op import sqlalchemy as sa revision = "20260822_sales_customer_learning_p15" down_revision = "20260822_sales_income_foundation_p14" branch_labels = None depends_on = None def upgrade(): op.create_table( "accounting_sales_historical_runs", 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("tally_guid", sa.String(120), nullable=False, server_default=""), sa.Column("company_name", sa.String(255), nullable=False, server_default=""), sa.Column("workstation_agent_id", sa.Integer(), sa.ForeignKey("erp_workstation_agents.id", ondelete="SET NULL"), nullable=True), sa.Column("agent_job_id", sa.Integer(), sa.ForeignKey("erp_agent_jobs.id", ondelete="SET NULL"), nullable=True), sa.Column("date_from", sa.String(20), nullable=False, server_default=""), sa.Column("date_to", sa.String(20), nullable=False, server_default=""), sa.Column("status", sa.String(30), nullable=False, server_default="queued"), sa.Column("evidence_rows", sa.Integer(), nullable=False, server_default="0"), sa.Column("error_message", sa.Text(), nullable=True), sa.Column("requested_by_user_id", sa.Integer(), sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True), sa.Column("created_at_utc", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.Column("completed_at_utc", sa.DateTime(timezone=True), nullable=True), ) for name in ("tenant_id","client_id","tally_guid","workstation_agent_id","agent_job_id","status","created_at_utc"): op.create_index(f"ix_accounting_sales_historical_runs_{name}", "accounting_sales_historical_runs", [name]) op.create_table( "accounting_sales_historical_evidence", 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("tally_guid", sa.String(120), nullable=False, server_default=""), sa.Column("company_name", sa.String(255), nullable=False, server_default=""), sa.Column("party_ledger_name", sa.String(255), nullable=False), sa.Column("party_parent_group", sa.String(255), nullable=False, server_default=""), sa.Column("counter_ledger_name", sa.String(255), nullable=False), sa.Column("counter_parent_group", sa.String(255), nullable=False, server_default=""), sa.Column("voucher_type_name", sa.String(160), nullable=False, server_default="Sales"), sa.Column("voucher_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("absolute_amount_total", sa.Float(), nullable=False, server_default="0"), sa.Column("first_voucher_date", sa.String(20), nullable=True), sa.Column("last_voucher_date", sa.String(20), nullable=True), sa.Column("sample_narration", sa.Text(), nullable=True), sa.Column("refreshed_at_utc", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.UniqueConstraint( "tenant_id","client_id","tally_guid","party_ledger_name","counter_ledger_name","voucher_type_name", name="uq_sales_hist_evidence", ), ) for name in ("tenant_id","client_id","tally_guid","party_ledger_name","counter_ledger_name","voucher_type_name","refreshed_at_utc"): op.create_index(f"ix_accounting_sales_historical_evidence_{name}", "accounting_sales_historical_evidence", [name]) op.create_table( "accounting_sales_customer_mappings", 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("customer_name", sa.String(255), nullable=False, server_default=""), sa.Column("normalized_customer_name", sa.String(255), nullable=False, server_default=""), sa.Column("customer_gstin", sa.String(20), nullable=False, server_default=""), sa.Column("identity_key", sa.String(300), nullable=False), sa.Column("tally_guid", sa.String(120), nullable=False, server_default=""), sa.Column("tally_customer_ledger_name", sa.String(255), nullable=False, server_default=""), sa.Column("nature_id", sa.Integer(), sa.ForeignKey("accounting_natures.id", ondelete="SET NULL"), nullable=True), sa.Column("sales_ledger_name", sa.String(255), nullable=False, server_default=""), sa.Column("source", sa.String(30), nullable=False, server_default="review"), sa.Column("evidence_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("confidence_percent", sa.Integer(), nullable=False, server_default="0"), sa.Column("total_amount", sa.Float(), nullable=False, server_default="0"), sa.Column("last_seen_date", sa.String(20), nullable=False, server_default=""), sa.Column("confirmed_by_user_id", sa.Integer(), sa.ForeignKey("users.id", ondelete="SET NULL"), nullable=True), sa.Column("confirmed_at_utc", sa.DateTime(timezone=True), nullable=True), sa.Column("created_at_utc", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.Column("updated_at_utc", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.UniqueConstraint("tenant_id","client_id","identity_key", name="uq_sales_customer_mapping_identity"), ) for name in ("tenant_id","client_id","normalized_customer_name","customer_gstin","identity_key","tally_guid","nature_id","source","created_at_utc"): op.create_index(f"ix_accounting_sales_customer_mappings_{name}", "accounting_sales_customer_mappings", [name]) def downgrade(): op.drop_table("accounting_sales_customer_mappings") op.drop_table("accounting_sales_historical_evidence") op.drop_table("accounting_sales_historical_runs")