"""add persistent client service subscription master Revision ID: 20260805_client_service_plan_master Revises: 20260805_task_instance_period_label """ from alembic import op import sqlalchemy as sa from sqlalchemy import inspect revision="20260805_client_service_plan_master" down_revision="20260805_task_instance_period_label" branch_labels=None depends_on=None def upgrade(): bind=op.get_bind(); insp=inspect(bind); tables=set(insp.get_table_names()) if "client_service_plans" not in tables: op.create_table("client_service_plans", sa.Column("id",sa.Integer(),primary_key=True),sa.Column("tenant_id",sa.Integer(),sa.ForeignKey("tenants.id",ondelete="CASCADE"),nullable=False),sa.Column("branch_id",sa.Integer(),sa.ForeignKey("branches.id",ondelete="SET NULL")),sa.Column("client_id",sa.Integer(),sa.ForeignKey("clients.id",ondelete="CASCADE"),nullable=False),sa.Column("service_catalogue_id",sa.Integer(),sa.ForeignKey("service_catalogues.id",ondelete="CASCADE"),nullable=False),sa.Column("firm_service_selection_id",sa.Integer(),sa.ForeignKey("firm_service_selections.id",ondelete="SET NULL")),sa.Column("default_partner_user_id",sa.Integer(),sa.ForeignKey("users.id")),sa.Column("default_performing_partner_user_id",sa.Integer(),sa.ForeignKey("users.id")),sa.Column("default_manager_user_id",sa.Integer(),sa.ForeignKey("users.id")),sa.Column("default_staff_user_id",sa.Integer(),sa.ForeignKey("users.id")),sa.Column("default_review_partner_user_id",sa.Integer(),sa.ForeignKey("users.id")),sa.Column("recurrence_type",sa.String(30),nullable=False,server_default="one_time"),sa.Column("effective_from",sa.Date()),sa.Column("effective_to",sa.Date()),sa.Column("auto_generate_periods",sa.Boolean(),nullable=False,server_default=sa.text("false")),sa.Column("status",sa.String(30),nullable=False,server_default="active"),sa.Column("is_active",sa.Boolean(),nullable=False,server_default=sa.text("true")),sa.Column("remarks",sa.Text()),sa.Column("created_by_user_id",sa.Integer(),sa.ForeignKey("users.id")),sa.Column("updated_by_user_id",sa.Integer(),sa.ForeignKey("users.id")),sa.Column("created_at_utc",sa.DateTime(timezone=True),nullable=False,server_default=sa.text("CURRENT_TIMESTAMP")),sa.Column("updated_at_utc",sa.DateTime(timezone=True),nullable=False,server_default=sa.text("CURRENT_TIMESTAMP")),sa.UniqueConstraint("tenant_id","client_id","service_catalogue_id",name="uq_csp_tenant_client_service")) for col in ("tenant_id","branch_id","client_id","service_catalogue_id","default_partner_user_id","default_performing_partner_user_id","default_manager_user_id","default_staff_user_id","default_review_partner_user_id","recurrence_type","status","is_active"): op.create_index(f"ix_csp_{col}","client_service_plans",[col]) cols={c["name"] for c in inspect(bind).get_columns("client_service_subscriptions")} if "service_plan_id" not in cols: op.add_column("client_service_subscriptions",sa.Column("service_plan_id",sa.Integer(),nullable=True)) op.create_foreign_key("fk_css_service_plan","client_service_subscriptions","client_service_plans",["service_plan_id"],["id"],ondelete="SET NULL") op.create_index("ix_css_service_plan_id","client_service_subscriptions",["service_plan_id"]) op.execute(sa.text("""INSERT INTO client_service_plans (tenant_id,branch_id,client_id,service_catalogue_id,firm_service_selection_id,default_partner_user_id,default_performing_partner_user_id,default_manager_user_id,default_staff_user_id,default_review_partner_user_id,recurrence_type,status,is_active,remarks,created_by_user_id,updated_by_user_id) SELECT DISTINCT ON (s.tenant_id,s.client_id,s.service_catalogue_id) s.tenant_id,s.branch_id,s.client_id,s.service_catalogue_id,s.firm_service_selection_id,s.assigned_partner_user_id,s.performing_partner_user_id,s.assigned_manager_user_id,s.assigned_staff_user_id,s.review_partner_user_id,COALESCE(c.recurrence_type,'one_time'),'active',true,s.remarks,s.created_by_user_id,s.updated_by_user_id FROM client_service_subscriptions s JOIN service_catalogues c ON c.id=s.service_catalogue_id LEFT JOIN client_service_plans p ON p.tenant_id=s.tenant_id AND p.client_id=s.client_id AND p.service_catalogue_id=s.service_catalogue_id WHERE p.id IS NULL ORDER BY s.tenant_id,s.client_id,s.service_catalogue_id,s.id DESC""")) op.execute(sa.text("""UPDATE client_service_subscriptions s SET service_plan_id=p.id FROM client_service_plans p WHERE s.service_plan_id IS NULL AND p.tenant_id=s.tenant_id AND p.client_id=s.client_id AND p.service_catalogue_id=s.service_catalogue_id""")) def downgrade(): bind=op.get_bind(); cols={c["name"] for c in inspect(bind).get_columns("client_service_subscriptions")} if "service_plan_id" in cols: op.drop_index("ix_css_service_plan_id",table_name="client_service_subscriptions") op.drop_constraint("fk_css_service_plan","client_service_subscriptions",type_="foreignkey") op.drop_column("client_service_subscriptions","service_plan_id") if "client_service_plans" in inspect(bind).get_table_names(): op.drop_table("client_service_plans")