-- +goose Up CREATE TABLE IF NOT EXISTS b2b_language ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, created_at DATETIME(6) NOT NULL, updated_at DATETIME(6) NULL, deleted_at DATETIME(6) NULL, name VARCHAR(128) NOT NULL, iso_code VARCHAR(2) NOT NULL, lang_code VARCHAR(5) NOT NULL, date_format VARCHAR(32) NOT NULL, date_format_short VARCHAR(32) NOT NULL, rtl TINYINT(1) NOT NULL DEFAULT 0, is_default TINYINT(1) NOT NULL DEFAULT 0, active TINYINT(1) NOT NULL DEFAULT 1, flag VARCHAR(16) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE INDEX IF NOT EXISTS idx_language_deleted_at ON b2b_language (deleted_at); CREATE TABLE IF NOT EXISTS b2b_components ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE UNIQUE INDEX IF NOT EXISTS uk_components_name ON b2b_components (name, id); -- scopes CREATE TABLE IF NOT EXISTS b2b_scopes ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE UNIQUE INDEX IF NOT EXISTS uk_scopes_name ON b2b_scopes (name); -- translations CREATE TABLE IF NOT EXISTS b2b_translations ( lang_id INT UNSIGNED NOT NULL, scope_id INT UNSIGNED NOT NULL, component_id INT UNSIGNED NOT NULL, `key` VARCHAR(255) NOT NULL, data TEXT NULL, PRIMARY KEY (lang_id, scope_id, component_id, `key`), CONSTRAINT fk_translations_language FOREIGN KEY (lang_id) REFERENCES b2b_language(id) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT fk_translations_scope FOREIGN KEY (scope_id) REFERENCES b2b_scopes(id) ON DELETE RESTRICT ON UPDATE RESTRICT, CONSTRAINT fk_translations_component FOREIGN KEY (component_id) REFERENCES b2b_components(id) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE TABLE `b2b_roles` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(63) NULL ); CREATE TABLE b2b_permissions ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(255) NOT NULL UNIQUE ); CREATE TABLE b2b_role_permissions ( role_id BIGINT UNSIGNED NOT NULL, permission_id BIGINT UNSIGNED NOT NULL, PRIMARY KEY (role_id, permission_id), CONSTRAINT fk_role_permissions_role FOREIGN KEY (role_id) REFERENCES b2b_roles(id) ON DELETE CASCADE, CONSTRAINT fk_role_permissions_permission FOREIGN KEY (permission_id) REFERENCES b2b_permissions(id) ON DELETE CASCADE ); CREATE TABLE `b2b_top_menu_roles` ( `top_menu_id` INT NOT NULL, `role_id` BIGINT UNSIGNED NOT NULL, PRIMARY KEY (`top_menu_id`, `role_id`), CONSTRAINT fk_top_menu_roles_menu FOREIGN KEY (`top_menu_id`) REFERENCES `b2b_top_menu`(`menu_id`) ON DELETE CASCADE, CONSTRAINT fk_top_menu_roles_role FOREIGN KEY (`role_id`) REFERENCES `b2b_roles`(`id`) ON DELETE CASCADE ); -- customers CREATE TABLE IF NOT EXISTS b2b_customers ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL, password VARCHAR(255) NULL, first_name VARCHAR(100) NULL, last_name VARCHAR(100) NULL, role_id BIGINT UNSIGNED NOT NULL DEFAULT 1, provider VARCHAR(20) NULL DEFAULT 'local', provider_id VARCHAR(255) NULL, avatar_url VARCHAR(500) NULL, is_active TINYINT(1) NULL DEFAULT 1, email_verified TINYINT(1) NULL DEFAULT 0, email_verification_token VARCHAR(255) NULL, email_verification_expires DATETIME(6) NULL, password_reset_token VARCHAR(255) NULL, password_reset_expires DATETIME(6) NULL, webdav_token VARCHAR(255) NULL, webdav_expires DATETIME(6) NULL, last_password_reset_request DATETIME(6) NULL, last_login_at DATETIME(6) NULL, lang_id INT NULL DEFAULT 2, country_id INT NULL DEFAULT 2, created_at DATETIME(6) NULL, updated_at DATETIME(6) NULL, deleted_at DATETIME(6) NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE UNIQUE INDEX IF NOT EXISTS idx_customers_email ON b2b_customers (email); CREATE INDEX IF NOT EXISTS idx_customers_deleted_at ON b2b_customers (deleted_at); CREATE INDEX IF NOT EXISTS idx_customers_webdav_token ON b2b_customers (webdav_token); ALTER TABLE b2b_customers ADD CONSTRAINT fk_customer_role FOREIGN KEY (role_id) REFERENCES b2b_roles(id); -- customer_carts CREATE TABLE IF NOT EXISTS b2b_customer_carts ( cart_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, name VARCHAR(255) NULL, CONSTRAINT fk_customer_carts_customers FOREIGN KEY (user_id) REFERENCES b2b_customers(id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE INDEX IF NOT EXISTS idx_customer_carts_user_id ON b2b_customer_carts (user_id); -- carts_products CREATE TABLE IF NOT EXISTS b2b_carts_products ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, cart_id INT UNSIGNED NOT NULL, product_id INT UNSIGNED NOT NULL, product_attribute_id INT NULL, amount INT UNSIGNED NOT NULL, CONSTRAINT fk_carts_products_customer_carts FOREIGN KEY (cart_id) REFERENCES b2b_customer_carts (cart_id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_carts_products_product FOREIGN KEY (product_id) REFERENCES ps_product (id_product) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE INDEX IF NOT EXISTS idx_carts_products_cart_id ON b2b_carts_products (cart_id); -- favorites CREATE TABLE IF NOT EXISTS b2b_favorites ( user_id BIGINT UNSIGNED NOT NULL, product_id INT UNSIGNED NOT NULL, PRIMARY KEY (user_id, product_id), CONSTRAINT fk_favorites_customer FOREIGN KEY (user_id) REFERENCES b2b_customers(id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_favorites_product FOREIGN KEY (product_id) REFERENCES ps_product(id_product) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; -- refresh_tokens CREATE TABLE IF NOT EXISTS b2b_refresh_tokens ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, customer_id BIGINT UNSIGNED NOT NULL, token_hash VARCHAR(64) NOT NULL, expires_at DATETIME(6) NOT NULL, created_at DATETIME(6) NOT NULL, CONSTRAINT fk_refresh_tokens_customer FOREIGN KEY (customer_id) REFERENCES b2b_customers(id) ON DELETE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE UNIQUE INDEX IF NOT EXISTS uk_refresh_tokens_token_hash ON b2b_refresh_tokens (token_hash); CREATE INDEX IF NOT EXISTS idx_refresh_tokens_customer_id ON b2b_refresh_tokens (customer_id); CREATE TABLE `b2b_currencies` ( `id` BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, `ps_id_currency` INT UNSIGNED NOT NULL, `is_default` TINYINT NOT NULL, `is_active` TINYINT NOT NULL, PRIMARY KEY (`id`) ) ENGINE = InnoDB; ALTER TABLE `b2b_currencies` ADD CONSTRAINT `FK_b2b_currencies_ps_id_currency` FOREIGN KEY (`ps_id_currency`) REFERENCES `ps_currency` (`id_currency`) ON DELETE CASCADE ON UPDATE CASCADE; CREATE INDEX `fk_b2b_currencies_ps_currency` ON `b2b_currencies` ( `ps_id_currency` ASC ); CREATE TABLE `b2b_currency_rates` ( `id` BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, `b2b_id_currency` BIGINT UNSIGNED NOT NULL, `created_at` DATETIME NOT NULL, `conversion_rate` DECIMAL(13,6) NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE = InnoDB; ALTER TABLE `b2b_currency_rates` ADD CONSTRAINT `FK_b2b_currency_rates_b2b_id_currency` FOREIGN KEY (`b2b_id_currency`) REFERENCES `b2b_currencies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; CREATE INDEX `fk_b2b_currency_rates_b2b_currencies` ON `b2b_currency_rates` ( `b2b_id_currency` ASC ); -- countries CREATE TABLE IF NOT EXISTS b2b_countries ( `id` BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, `flag` VARCHAR(16) NOT NULL, `ps_id_country` INT UNSIGNED NOT NULL, `b2b_id_currency` BIGINT UNSIGNED NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `fk_b2b_countries_ps_country` FOREIGN KEY (`ps_id_country`) REFERENCES `ps_country` (`id_country`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `FK_b2b_countries_b2b_id_currency` FOREIGN KEY (`b2b_id_currency`) REFERENCES `b2b_currencies` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE = InnoDB; CREATE INDEX `fk_b2b_countries_ps_country` ON `b2b_countries` ( `ps_id_country` ASC ); -- addresses CREATE TABLE IF NOT EXISTS b2b_addresses ( id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, b2b_customer_id BIGINT UNSIGNED NOT NULL, address_info TEXT NOT NULL, b2b_country_id BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id), CONSTRAINT fk_b2b_addresses_b2b_customers FOREIGN KEY (b2b_customer_id) REFERENCES b2b_customers (id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_b2b_addresses_b2b_countries FOREIGN KEY (b2b_country_id) REFERENCES b2b_countries (id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE = InnoDB; CREATE TABLE b2b_specific_price ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, created_at DATETIME NULL, updated_at DATETIME NULL, deleted_at DATETIME NULL, valid_from DATETIME NULL, valid_till DATETIME NULL, has_expiration_date BOOLEAN DEFAULT FALSE, reduction_type ENUM('amount', 'percentage') NOT NULL, price DECIMAL(10, 2) NULL, b2b_id_currency BIGINT UNSIGNED NULL, -- specifies which currency is used for the price percentage_reduction DECIMAL(5, 2) NULL, from_quantity INT UNSIGNED DEFAULT 1, is_active BOOLEAN DEFAULT TRUE ) ENGINE = InnoDB; CREATE INDEX idx_b2b_active_dates ON b2b_specific_price(is_active, valid_from, valid_till); CREATE INDEX idx_b2b_lookup ON b2b_specific_price ( is_active, from_quantity ); CREATE TABLE b2b_specific_price_product ( b2b_specific_price_id BIGINT UNSIGNED, id_product INT UNSIGNED, PRIMARY KEY (b2b_specific_price_id, id_product), FOREIGN KEY (b2b_specific_price_id) REFERENCES b2b_specific_price(id) ON DELETE CASCADE, FOREIGN KEY (id_product) REFERENCES ps_product(id_product) ON DELETE CASCADE ); CREATE TABLE b2b_specific_price_category ( b2b_specific_price_id BIGINT UNSIGNED, id_category INT UNSIGNED, PRIMARY KEY (b2b_specific_price_id, id_category), FOREIGN KEY (b2b_specific_price_id) REFERENCES b2b_specific_price(id) ON DELETE CASCADE, FOREIGN KEY (id_category) REFERENCES ps_category(id_category) ON DELETE CASCADE ); CREATE TABLE b2b_specific_price_product_attribute ( b2b_specific_price_id BIGINT UNSIGNED, id_product_attribute INT UNSIGNED, PRIMARY KEY (b2b_specific_price_id, id_product_attribute), FOREIGN KEY (b2b_specific_price_id) REFERENCES b2b_specific_price(id) ON DELETE CASCADE, FOREIGN KEY (id_product_attribute) REFERENCES ps_product_attribute(id_product_attribute) ON DELETE CASCADE ); CREATE TABLE b2b_specific_price_customer ( b2b_specific_price_id BIGINT UNSIGNED, b2b_id_customer BIGINT UNSIGNED, PRIMARY KEY (b2b_specific_price_id, b2b_id_customer), FOREIGN KEY (b2b_specific_price_id) REFERENCES b2b_specific_price(id) ON DELETE CASCADE, FOREIGN KEY (b2b_id_customer) REFERENCES b2b_customers(id) ON DELETE CASCADE ); CREATE TABLE b2b_specific_price_country ( b2b_specific_price_id BIGINT UNSIGNED, b2b_id_country BIGINT UNSIGNED, PRIMARY KEY (b2b_specific_price_id, b2b_id_country), FOREIGN KEY (b2b_specific_price_id) REFERENCES b2b_specific_price(id) ON DELETE CASCADE, FOREIGN KEY (b2b_id_country) REFERENCES b2b_countries(id) ON DELETE CASCADE ); CREATE INDEX idx_b2b_product_rel ON b2b_specific_price_product (id_product); CREATE INDEX idx_b2b_category_rel ON b2b_specific_price_category (id_category); CREATE INDEX idx_b2b_product_attribute_rel ON b2b_specific_price_product_attribute (id_product_attribute); CREATE INDEX idx_bsp_customer_rel ON b2b_specific_price_customer (b2b_id_customer); CREATE INDEX idx_bsp_country_rel ON b2b_specific_price_country (b2b_id_country); CREATE TABLE b2b_route_roles ( route_id INT NOT NULL, role_id BIGINT UNSIGNED NOT NULL, PRIMARY KEY (route_id, role_id), INDEX idx_role_id (role_id), INDEX idx_route_id (route_id), CONSTRAINT FK_b2b_route_roles_route_id FOREIGN KEY (route_id) REFERENCES b2b_routes (id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT FK_b2b_route_roles_role_id FOREIGN KEY (role_id) REFERENCES b2b_roles (id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB; DELIMITER // CREATE FUNCTION IF NOT EXISTS slugify_eu(input TEXT) RETURNS TEXT DETERMINISTIC BEGIN DECLARE s TEXT; SET s = LOWER(input); -- spaces SET s = REPLACE(s,' ','_'); -- Polish SET s = REPLACE(s,'ą','a'); SET s = REPLACE(s,'ć','c'); SET s = REPLACE(s,'ę','e'); SET s = REPLACE(s,'ł','l'); SET s = REPLACE(s,'ń','n'); SET s = REPLACE(s,'ó','o'); SET s = REPLACE(s,'ś','s'); SET s = REPLACE(s,'ż','z'); SET s = REPLACE(s,'ź','z'); -- German SET s = REPLACE(s,'ä','a'); SET s = REPLACE(s,'ö','o'); SET s = REPLACE(s,'ü','u'); SET s = REPLACE(s,'ß','ss'); -- French SET s = REPLACE(s,'à','a'); SET s = REPLACE(s,'â','a'); SET s = REPLACE(s,'æ','ae'); SET s = REPLACE(s,'ç','c'); SET s = REPLACE(s,'è','e'); SET s = REPLACE(s,'é','e'); SET s = REPLACE(s,'ê','e'); SET s = REPLACE(s,'ë','e'); SET s = REPLACE(s,'î','i'); SET s = REPLACE(s,'ï','i'); SET s = REPLACE(s,'ô','o'); SET s = REPLACE(s,'ù','u'); SET s = REPLACE(s,'û','u'); SET s = REPLACE(s,'ü','u'); SET s = REPLACE(s,'ÿ','y'); -- Spanish / Portuguese SET s = REPLACE(s,'á','a'); SET s = REPLACE(s,'í','i'); SET s = REPLACE(s,'ñ','n'); -- Scandinavian SET s = REPLACE(s,'å','a'); SET s = REPLACE(s,'ø','o'); RETURN s; END // DELIMITER ; DELIMITER $$ CREATE FUNCTION IF NOT EXISTS get_subcategories(startCategory INT) RETURNS TEXT DETERMINISTIC BEGIN DECLARE result TEXT; -- Use GROUP_CONCAT to aggregate all IDs into a single string WITH RECURSIVE subcategories AS ( SELECT c.id_category FROM ps_category c WHERE c.id_category = startCategory UNION ALL SELECT c.id_category FROM ps_category c INNER JOIN subcategories sc ON c.id_parent = sc.id_category ) SELECT GROUP_CONCAT(id_category) INTO result FROM subcategories; RETURN result; END$$ DELIMITER ; -- +goose Down DROP TABLE IF EXISTS b2b_addresses; DROP TABLE IF EXISTS b2b_top_menu_roles; DROP TABLE IF EXISTS b2b_favorites; DROP TABLE IF EXISTS b2b_carts_products; DROP TABLE IF EXISTS b2b_customer_carts; DROP TABLE IF EXISTS b2b_specific_price_country; DROP TABLE IF EXISTS b2b_specific_price_customer; DROP TABLE IF EXISTS b2b_specific_price_product_attribute; DROP TABLE IF EXISTS b2b_route_roles; DROP TABLE IF EXISTS b2b_specific_price_category; DROP TABLE IF EXISTS b2b_specific_price_product; DROP TABLE IF EXISTS b2b_specific_price; DROP TABLE IF EXISTS b2b_role_permissions; DROP TABLE IF EXISTS b2b_permissions; DROP TABLE IF EXISTS b2b_roles; DROP TABLE IF EXISTS b2b_countries; DROP TABLE IF EXISTS b2b_currency_rates; DROP TABLE IF EXISTS b2b_currencies; DROP TABLE IF EXISTS b2b_refresh_tokens; DROP TABLE IF EXISTS b2b_customers; DROP TABLE IF EXISTS b2b_translations; DROP TABLE IF EXISTS b2b_scopes; DROP TABLE IF EXISTS b2b_components; DROP TABLE IF EXISTS b2b_language;