"""Scope-aware client services using the existing registrations module.""" from alembic import op import sqlalchemy as sa revision = "20260805_scope_aware_services" down_revision = "20260805_client_service_plan_master" branch_labels = None depends_on = None def upgrade(): op.create_table( "client_business_units", sa.Column("id", sa.Integer(), primary_key=True, autoincrement=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("business_code", sa.String(50), nullable=False), sa.Column("business_name", sa.String(200), nullable=False), sa.Column("trade_name", sa.String(200), nullable=True), sa.Column("nature_of_business", sa.String(200), nullable=True), sa.Column("is_primary", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()), sa.Column("created_by_user_id", sa.Integer(), sa.ForeignKey("users.id"), nullable=True), sa.Column("updated_by_user_id", sa.Integer(), sa.ForeignKey("users.id"), 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", "business_code", name="uq_cbu_tenant_client_code"), ) op.create_index("ix_client_business_units_tenant_id", "client_business_units", ["tenant_id"]) op.create_index("ix_client_business_units_client_id", "client_business_units", ["client_id"]) op.create_table( "client_branches", sa.Column("id", sa.Integer(), primary_key=True, autoincrement=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("business_unit_id", sa.Integer(), sa.ForeignKey("client_business_units.id", ondelete="CASCADE"), nullable=False), sa.Column("branch_code", sa.String(50), nullable=False), sa.Column("branch_name", sa.String(200), nullable=False), sa.Column("branch_type", sa.String(40), nullable=False, server_default="branch"), sa.Column("address_line_1", sa.String(255), nullable=True), sa.Column("address_line_2", sa.String(255), nullable=True), sa.Column("city", sa.String(100), nullable=True), sa.Column("state", sa.String(100), nullable=True), sa.Column("pincode", sa.String(20), nullable=True), sa.Column("is_primary", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()), sa.Column("created_by_user_id", sa.Integer(), sa.ForeignKey("users.id"), nullable=True), sa.Column("updated_by_user_id", sa.Integer(), sa.ForeignKey("users.id"), 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", "business_unit_id", "branch_code", name="uq_cbranch_tenant_business_code"), ) op.create_index("ix_client_branches_tenant_id", "client_branches", ["tenant_id"]) op.create_index("ix_client_branches_client_id", "client_branches", ["client_id"]) op.create_index("ix_client_branches_business_unit_id", "client_branches", ["business_unit_id"]) # Reuse and extend the production client_registrations table introduced in Phase 4. with op.batch_alter_table("client_registrations") as batch: batch.add_column(sa.Column("business_unit_id", sa.Integer(), nullable=True)) batch.add_column(sa.Column("client_branch_id", sa.Integer(), nullable=True)) batch.add_column(sa.Column("legal_name", sa.String(200), nullable=True)) batch.add_column(sa.Column("trade_name", sa.String(200), nullable=True)) batch.add_column(sa.Column("state", sa.String(100), nullable=True)) batch.create_foreign_key( "fk_client_registrations_business_unit", "client_business_units", ["business_unit_id"], ["id"], ondelete="SET NULL" ) batch.create_foreign_key( "fk_client_registrations_client_branch", "client_branches", ["client_branch_id"], ["id"], ondelete="SET NULL" ) batch.create_index("ix_client_registrations_business_unit_id", ["business_unit_id"]) batch.create_index("ix_client_registrations_client_branch_id", ["client_branch_id"]) with op.batch_alter_table("service_catalogues") as batch: batch.add_column(sa.Column("service_scope_type", sa.String(30), nullable=False, server_default="client")) batch.add_column(sa.Column("required_registration_type", sa.String(40), nullable=True)) batch.create_index("ix_service_catalogues_service_scope_type", ["service_scope_type"]) batch.create_index("ix_service_catalogues_required_registration_type", ["required_registration_type"]) for table in ("client_service_plans", "client_service_subscriptions"): with op.batch_alter_table(table) as batch: batch.add_column(sa.Column("scope_type", sa.String(30), nullable=False, server_default="client")) batch.add_column(sa.Column("scope_key", sa.String(80), nullable=False, server_default="")) batch.add_column(sa.Column("business_unit_id", sa.Integer(), nullable=True)) batch.add_column(sa.Column("client_branch_id", sa.Integer(), nullable=True)) batch.add_column(sa.Column("registration_id", sa.Integer(), nullable=True)) batch.create_foreign_key(f"fk_{table}_business_unit", "client_business_units", ["business_unit_id"], ["id"], ondelete="SET NULL") batch.create_foreign_key(f"fk_{table}_client_branch", "client_branches", ["client_branch_id"], ["id"], ondelete="SET NULL") batch.create_foreign_key(f"fk_{table}_registration", "client_registrations", ["registration_id"], ["id"], ondelete="SET NULL") batch.create_index(f"ix_{table}_scope_type", ["scope_type"]) batch.create_index(f"ix_{table}_scope_key", ["scope_key"]) batch.create_index(f"ix_{table}_business_unit_id", ["business_unit_id"]) batch.create_index(f"ix_{table}_client_branch_id", ["client_branch_id"]) batch.create_index(f"ix_{table}_registration_id", ["registration_id"]) op.execute("UPDATE client_service_plans SET scope_key = 'CLIENT:' || client_id WHERE scope_key = ''") op.execute("UPDATE client_service_subscriptions SET scope_key = 'CLIENT:' || client_id WHERE scope_key = ''") with op.batch_alter_table("client_service_plans") as batch: batch.drop_constraint("uq_csp_tenant_client_service", type_="unique") batch.create_unique_constraint("uq_csp_tenant_service_scope", ["tenant_id", "service_catalogue_id", "scope_key"]) with op.batch_alter_table("client_service_subscriptions") as batch: batch.drop_constraint("uq_css_tenant_client_service_fy_period", type_="unique") batch.create_unique_constraint( "uq_css_tenant_service_scope_fy_period", ["tenant_id", "service_catalogue_id", "scope_key", "financial_year", "period_label"], ) def downgrade(): with op.batch_alter_table("client_service_subscriptions") as batch: batch.drop_constraint("uq_css_tenant_service_scope_fy_period", type_="unique") batch.create_unique_constraint( "uq_css_tenant_client_service_fy_period", ["tenant_id", "client_id", "service_catalogue_id", "financial_year", "period_label"], ) for name in ("registration_id", "client_branch_id", "business_unit_id", "scope_key", "scope_type"): batch.drop_column(name) with op.batch_alter_table("client_service_plans") as batch: batch.drop_constraint("uq_csp_tenant_service_scope", type_="unique") batch.create_unique_constraint("uq_csp_tenant_client_service", ["tenant_id", "client_id", "service_catalogue_id"]) for name in ("registration_id", "client_branch_id", "business_unit_id", "scope_key", "scope_type"): batch.drop_column(name) with op.batch_alter_table("service_catalogues") as batch: batch.drop_column("required_registration_type") batch.drop_column("service_scope_type") with op.batch_alter_table("client_registrations") as batch: for name in ("state", "trade_name", "legal_name", "client_branch_id", "business_unit_id"): batch.drop_column(name) op.drop_table("client_branches") op.drop_table("client_business_units")