-- Category variations & options (product categories only) -- Variation types: color (predefined palette), size (user-defined), custom (user-defined name + values) CREATE TYPE variation_type AS ENUM ('color', 'size', 'custom'); CREATE TABLE category_variations ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, business_id BIGINT NOT NULL, category_id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, variation_type variation_type NOT NULL, sort_order INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT category_variations_business_id_fkey FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE, CONSTRAINT category_variations_category_id_fkey FOREIGN KEY (category_id) REFERENCES categories (id) ON DELETE CASCADE ); CREATE UNIQUE INDEX category_variations_one_color_per_category ON category_variations (category_id) WHERE variation_type = 'color'; CREATE UNIQUE INDEX category_variations_one_size_per_category ON category_variations (category_id) WHERE variation_type = 'size'; CREATE UNIQUE INDEX category_variations_custom_name_per_category ON category_variations (category_id, name) WHERE variation_type = 'custom'; CREATE INDEX idx_category_variations_category_id ON category_variations (category_id); CREATE INDEX idx_category_variations_business_id ON category_variations (business_id); CREATE TRIGGER category_variations_set_updated_at BEFORE UPDATE ON category_variations FOR EACH ROW EXECUTE FUNCTION set_updated_at(); CREATE TABLE category_variation_options ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, variation_id BIGINT NOT NULL, label VARCHAR(255) NOT NULL, value VARCHAR(255) NOT NULL, color_hex VARCHAR(7), sort_order INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT category_variation_options_variation_id_fkey FOREIGN KEY (variation_id) REFERENCES category_variations (id) ON DELETE CASCADE, CONSTRAINT category_variation_options_unique_value UNIQUE (variation_id, value) ); CREATE INDEX idx_category_variation_options_variation_id ON category_variation_options (variation_id); -- Product variants (combinations of category variation options) CREATE TABLE product_variants ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, business_id BIGINT NOT NULL, product_id BIGINT NOT NULL, sku VARCHAR(100), price NUMERIC(12, 2), compare_at_price NUMERIC(12, 2), stock_quantity INTEGER, is_active BOOLEAN NOT NULL DEFAULT TRUE, sort_order INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT product_variants_business_id_fkey FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE, CONSTRAINT product_variants_product_id_fkey FOREIGN KEY (product_id) REFERENCES products (id) ON DELETE CASCADE, CONSTRAINT product_variants_stock_non_negative CHECK (stock_quantity IS NULL OR stock_quantity >= 0) ); CREATE INDEX idx_product_variants_product_id ON product_variants (product_id); CREATE INDEX idx_product_variants_business_id ON product_variants (business_id); CREATE TRIGGER product_variants_set_updated_at BEFORE UPDATE ON product_variants FOR EACH ROW EXECUTE FUNCTION set_updated_at(); CREATE TABLE product_variant_selections ( variant_id BIGINT NOT NULL, variation_id BIGINT NOT NULL, option_id BIGINT NOT NULL, CONSTRAINT product_variant_selections_pkey PRIMARY KEY (variant_id, variation_id), CONSTRAINT product_variant_selections_variant_id_fkey FOREIGN KEY (variant_id) REFERENCES product_variants (id) ON DELETE CASCADE, CONSTRAINT product_variant_selections_variation_id_fkey FOREIGN KEY (variation_id) REFERENCES category_variations (id) ON DELETE RESTRICT, CONSTRAINT product_variant_selections_option_id_fkey FOREIGN KEY (option_id) REFERENCES category_variation_options (id) ON DELETE RESTRICT, CONSTRAINT product_variant_selections_unique_option UNIQUE (variant_id, option_id) ); CREATE INDEX idx_product_variant_selections_option_id ON product_variant_selections (option_id);