-- Repair: finish cart/orders/store selections after partial 019/020 on production. -- Safe to re-run (IF NOT EXISTS / ON CONFLICT). Prefer running ordered migrations on a fresh DB. -- Applied on api.meshkee.com 2026-07-21 when 019 created carts but failed before orders, -- and 020 created store_items but failed before selections / cart remapping. DO $$ BEGIN CREATE TYPE order_status AS ENUM ( 'pending','confirmed','processing','shipped','delivered','cancelled' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; DO $$ BEGIN CREATE TYPE order_source AS ENUM ('website', 'admin'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TABLE IF NOT EXISTS store_item_variant_selections ( variant_id BIGINT NOT NULL, variation_id BIGINT NOT NULL, option_id BIGINT NOT NULL, CONSTRAINT store_item_variant_selections_pkey PRIMARY KEY (variant_id, variation_id), CONSTRAINT store_item_variant_selections_variant_id_fkey FOREIGN KEY (variant_id) REFERENCES store_item_variants (id) ON DELETE CASCADE, CONSTRAINT store_item_variant_selections_variation_id_fkey FOREIGN KEY (variation_id) REFERENCES category_variations (id) ON DELETE RESTRICT, CONSTRAINT store_item_variant_selections_option_id_fkey FOREIGN KEY (option_id) REFERENCES category_variation_options (id) ON DELETE RESTRICT, CONSTRAINT store_item_variant_selections_unique_option UNIQUE (variant_id, option_id) ); CREATE INDEX IF NOT EXISTS idx_store_item_variant_selections_option_id ON store_item_variant_selections (option_id); CREATE TABLE IF NOT EXISTS cart_items ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, cart_id BIGINT NOT NULL, store_item_variant_id BIGINT NOT NULL, quantity INT NOT NULL DEFAULT 1, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT cart_items_cart_id_fkey FOREIGN KEY (cart_id) REFERENCES carts (id) ON DELETE CASCADE, CONSTRAINT cart_items_store_item_variant_id_fkey FOREIGN KEY (store_item_variant_id) REFERENCES store_item_variants (id) ON DELETE CASCADE, CONSTRAINT cart_items_cart_store_item_variant_unique UNIQUE (cart_id, store_item_variant_id), CONSTRAINT cart_items_quantity_positive CHECK (quantity > 0) ); CREATE INDEX IF NOT EXISTS idx_cart_items_cart_id ON cart_items (cart_id); CREATE INDEX IF NOT EXISTS idx_cart_items_store_item_variant_id ON cart_items (store_item_variant_id); CREATE TABLE IF NOT EXISTS orders ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, business_id BIGINT NOT NULL, user_id BIGINT NOT NULL, order_number VARCHAR(30) NOT NULL, status order_status NOT NULL DEFAULT 'pending', source order_source NOT NULL DEFAULT 'website', subtotal NUMERIC(12, 2) NOT NULL DEFAULT 0, shipping_total NUMERIC(12, 2) NOT NULL DEFAULT 0, discount_total NUMERIC(12, 2) NOT NULL DEFAULT 0, total NUMERIC(12, 2) NOT NULL DEFAULT 0, shipping_address JSONB NOT NULL DEFAULT '{}', address_id BIGINT, customer_notes TEXT, admin_notes TEXT, created_by BIGINT, process_step_id VARCHAR(64) NOT NULL DEFAULT 'processing', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT orders_business_id_fkey FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE, CONSTRAINT orders_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE RESTRICT, CONSTRAINT orders_address_id_fkey FOREIGN KEY (address_id) REFERENCES addresses (id) ON DELETE SET NULL, CONSTRAINT orders_created_by_fkey FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE SET NULL, CONSTRAINT orders_business_order_number_unique UNIQUE (business_id, order_number), CONSTRAINT orders_subtotal_non_negative CHECK (subtotal >= 0), CONSTRAINT orders_shipping_total_non_negative CHECK (shipping_total >= 0), CONSTRAINT orders_discount_total_non_negative CHECK (discount_total >= 0), CONSTRAINT orders_total_non_negative CHECK (total >= 0) ); CREATE INDEX IF NOT EXISTS idx_orders_business_created ON orders (business_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_orders_business_user ON orders (business_id, user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_orders_business_status ON orders (business_id, status, created_at DESC); CREATE INDEX IF NOT EXISTS idx_orders_business_process_step ON orders (business_id, process_step_id); CREATE TABLE IF NOT EXISTS order_items ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, order_id BIGINT NOT NULL, store_item_variant_id BIGINT, product_id BIGINT NOT NULL, product_title VARCHAR(255) NOT NULL, variant_sku VARCHAR(100), unit_price NUMERIC(12, 2) NOT NULL, compare_at_price NUMERIC(12, 2), quantity INT NOT NULL, line_total NUMERIC(12, 2) NOT NULL, selections_snapshot JSONB NOT NULL DEFAULT '[]', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT order_items_order_id_fkey FOREIGN KEY (order_id) REFERENCES orders (id) ON DELETE CASCADE, CONSTRAINT order_items_store_item_variant_id_fkey FOREIGN KEY (store_item_variant_id) REFERENCES store_item_variants (id) ON DELETE SET NULL, CONSTRAINT order_items_product_id_fkey FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE RESTRICT, CONSTRAINT order_items_quantity_positive CHECK (quantity > 0), CONSTRAINT order_items_unit_price_non_negative CHECK (unit_price >= 0), CONSTRAINT order_items_line_total_non_negative CHECK (line_total >= 0) ); CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items (order_id); CREATE INDEX IF NOT EXISTS idx_order_items_store_item_variant_id ON order_items (store_item_variant_id);