"""Add product_render_positions table and render_position_id on order_lines. Revision ID: 028 Revises: 027 Create Date: 2026-03-04 """ from alembic import op import sqlalchemy as sa revision = "028" down_revision = "027" branch_labels = None depends_on = None NIL_UUID = "00000000-0000-0000-0000-000000000000" def upgrade() -> None: # ── New table: product_render_positions ────────────────────────────────── op.execute(""" CREATE TABLE product_render_positions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE, name VARCHAR(200) NOT NULL, rotation_x DOUBLE PRECISION NOT NULL DEFAULT 0, rotation_y DOUBLE PRECISION NOT NULL DEFAULT 0, rotation_z DOUBLE PRECISION NOT NULL DEFAULT 0, is_default BOOLEAN NOT NULL DEFAULT false, sort_order INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW() ) """) op.execute( "CREATE UNIQUE INDEX uq_render_positions_product_name " "ON product_render_positions (product_id, lower(name))" ) op.execute("CREATE INDEX ix_render_positions_product_id ON product_render_positions (product_id)") # ── Add render_position_id to order_lines ──────────────────────────────── op.execute( "ALTER TABLE order_lines ADD COLUMN render_position_id UUID " "REFERENCES product_render_positions(id) ON DELETE SET NULL" ) # ── Update unique indexes to include position ───────────────────────────── op.execute("DROP INDEX IF EXISTS uq_order_lines_tracking") op.execute("DROP INDEX IF EXISTS uq_order_lines_render") op.execute( f"CREATE UNIQUE INDEX uq_order_lines_tracking " f"ON order_lines (order_id, product_id, COALESCE(render_position_id, '{NIL_UUID}'::uuid)) " f"WHERE output_type_id IS NULL" ) op.execute( f"CREATE UNIQUE INDEX uq_order_lines_render " f"ON order_lines (order_id, product_id, output_type_id, " f"COALESCE(render_position_id, '{NIL_UUID}'::uuid)) " f"WHERE output_type_id IS NOT NULL" ) def downgrade() -> None: # Restore original unique indexes (without position) op.execute("DROP INDEX IF EXISTS uq_order_lines_tracking") op.execute("DROP INDEX IF EXISTS uq_order_lines_render") op.execute("ALTER TABLE order_lines DROP COLUMN IF EXISTS render_position_id") op.execute( "CREATE UNIQUE INDEX uq_order_lines_tracking " "ON order_lines (order_id, product_id) " "WHERE output_type_id IS NULL" ) op.execute( "CREATE UNIQUE INDEX uq_order_lines_render " "ON order_lines (order_id, product_id, output_type_id) " "WHERE output_type_id IS NOT NULL" ) op.execute("DROP TABLE IF EXISTS product_render_positions CASCADE")