Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into storage

This commit is contained in:
Daniel Goc
2026-04-08 13:19:45 +02:00
57 changed files with 1839 additions and 276 deletions

View File

@@ -1,7 +1,7 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS b2b_language (
id INT AUTO_INCREMENT PRIMARY KEY,
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
created_at DATETIME(6) NOT NULL,
updated_at DATETIME(6) NULL,
deleted_at DATETIME(6) NULL,
@@ -19,29 +19,22 @@ CREATE TABLE IF NOT EXISTS b2b_language (
CREATE INDEX IF NOT EXISTS idx_language_deleted_at
ON b2b_language (deleted_at);
INSERT IGNORE INTO b2b_language
(id, created_at, updated_at, deleted_at, name, iso_code, lang_code, date_format, date_format_short, rtl, is_default, active, flag)
VALUES
(1, '2022-09-16 17:10:02.837', '2026-03-02 21:24:36.779730', NULL, 'Polski', 'pl', 'pl', '__-__-____', '__-__', 0, 0, 1, '🇵🇱'),
(2, '2022-09-16 17:10:02.852', '2026-03-02 21:24:36.779730', NULL, 'English', 'en', 'en', '__-__-____', '__-__', 0, 1, 1, '🇬🇧'),
(3, '2022-09-16 17:10:02.852', '2026-03-02 21:24:36.779730', NULL, 'Deutsch', 'de', 'de', '__-__-____', '__-__', 0, 0, 1, '🇩🇪');
CREATE TABLE IF NOT EXISTS b2b_components (
id INT AUTO_INCREMENT PRIMARY KEY,
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 AUTO_INCREMENT PRIMARY KEY,
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 NOT NULL,
scope_id INT NOT NULL,
component_id INT NOT NULL,
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`),
@@ -50,7 +43,49 @@ CREATE TABLE IF NOT EXISTS b2b_translations (
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 (
@@ -59,7 +94,7 @@ CREATE TABLE IF NOT EXISTS b2b_customers (
password VARCHAR(255) NULL,
first_name VARCHAR(100) NULL,
last_name VARCHAR(100) NULL,
role VARCHAR(20) NULL DEFAULT 'user',
role_id BIGINT UNSIGNED NOT NULL DEFAULT 1,
provider VARCHAR(20) NULL DEFAULT 'local',
provider_id VARCHAR(255) NULL,
avatar_url VARCHAR(500) NULL,
@@ -73,8 +108,8 @@ CREATE TABLE IF NOT EXISTS b2b_customers (
webdav_expires DATETIME(6) NULL,
last_password_reset_request DATETIME(6) NULL,
last_login_at DATETIME(6) NULL,
lang_id BIGINT NULL DEFAULT 2,
country_id BIGINT NULL DEFAULT 2,
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
@@ -89,9 +124,13 @@ 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 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
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
@@ -101,10 +140,10 @@ CREATE INDEX IF NOT EXISTS idx_customer_carts_user_id ON b2b_customer_carts (use
-- carts_products
CREATE TABLE IF NOT EXISTS b2b_carts_products (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
cart_id BIGINT UNSIGNED NOT NULL,
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
cart_id INT UNSIGNED NOT NULL,
product_id INT UNSIGNED NOT NULL,
product_attribute_id BIGINT 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
@@ -114,7 +153,7 @@ CREATE INDEX IF NOT EXISTS idx_carts_products_cart_id ON b2b_carts_products (car
-- refresh_tokens
CREATE TABLE IF NOT EXISTS b2b_refresh_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
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,
@@ -124,24 +163,133 @@ CREATE TABLE IF NOT EXISTS b2b_refresh_tokens (
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 INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(128) NOT NULL,
currency_id INT UNSIGNED NOT NULL,
flag VARCHAR(16) NOT NULL,
CONSTRAINT fk_countries_currency FOREIGN KEY (currency_id) REFERENCES ps_currency(id_currency) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`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
);
INSERT IGNORE INTO b2b_countries
(id, name, currency_id, flag)
VALUES
(1, 'Polska', 1, '🇵🇱'),
(2, 'England', 2, '🇬🇧'),
(3, 'Čeština', 2, '🇨🇿'),
(4, 'Deutschland', 2, '🇩🇪');
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,
scope ENUM('shop', 'category', 'product') NOT 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_scope ON b2b_specific_price(scope);
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 (
scope,
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
ON b2b_specific_price_customer (b2b_specific_price_id, b2b_id_customer);
CREATE INDEX idx_bsp_country
ON b2b_specific_price_country (b2b_specific_price_id, b2b_id_country);
DELIMITER //
@@ -242,3 +390,9 @@ DROP TABLE IF EXISTS b2b_scopes;
DROP TABLE IF EXISTS b2b_translations;
DROP TABLE IF EXISTS b2b_customers;
DROP TABLE IF EXISTS b2b_refresh_tokens;
DROP TABLE IF EXISTS b2b_currencies;
DROP TABLE IF EXISTS b2b_currency_rates;
DROP TABLE IF EXISTS b2b_specific_price;
DROP TABLE IF EXISTS b2b_specific_price_product;
DROP TABLE IF EXISTS b2b_specific_price_category;
DROP TABLE IF EXISTS b2b_specific_price_product_attribute;

View File

@@ -0,0 +1,46 @@
-- +goose Up
INSERT IGNORE INTO b2b_language
(id, created_at, updated_at, deleted_at, name, iso_code, lang_code, date_format, date_format_short, rtl, is_default, active, flag)
VALUES
(1, '2022-09-16 17:10:02.837', '2026-03-02 21:24:36.779730', NULL, 'Polski', 'pl', 'pl', '__-__-____', '__-__', 0, 0, 1, '🇵🇱'),
(2, '2022-09-16 17:10:02.852', '2026-03-02 21:24:36.779730', NULL, 'English', 'en', 'en', '__-__-____', '__-__', 0, 1, 1, '🇬🇧'),
(3, '2022-09-16 17:10:02.852', '2026-03-02 21:24:36.779730', NULL, 'Deutsch', 'de', 'de', '__-__-____', '__-__', 0, 0, 1, '🇩🇪');
INSERT INTO `b2b_roles` (`name`, `id`) VALUES ('user','1');
INSERT INTO `b2b_roles` (`name`, `id`) VALUES ('admin','2');
INSERT INTO `b2b_roles` (`name`, `id`) VALUES ('super_admin','3');
-- insert sample admin user admin@ma-al.com/Maal12345678
INSERT IGNORE INTO b2b_customers (id, email, password, first_name, last_name, role_id, provider, provider_id, avatar_url, is_active, email_verified, email_verification_token, email_verification_expires, password_reset_token, password_reset_expires, last_password_reset_request, last_login_at, lang_id, country_id, created_at, updated_at, deleted_at)
VALUES
(1, 'admin@ma-al.com', '$2a$10$Owy9DjrS0l3Fz4XoOvh5pulgmOMqdwXmb7hYE9BovnSuWS2plGr82', 'Super', 'Admin', 2, 'local', '', '', 1, 1, NULL, NULL, '', NULL, NULL, NULL, 1, 1, '2026-03-02 16:55:10.252740', '2026-03-02 16:55:10.252740', NULL);
ALTER TABLE b2b_customers AUTO_INCREMENT = 1;
INSERT INTO `b2b_currencies` (`ps_id_currency`, `is_default`, `is_active`) VALUES
('1','1','1'),
('2','0','1');
INSERT IGNORE INTO b2b_countries
(id, flag, ps_id_country, b2b_id_currency)
VALUES
(1, '🇵🇱', 14, 1),
(2, '🇬🇧', 17, 2),
(3, '🇨🇿', 16, 2),
(4, '🇩🇪', 1, 2);
INSERT INTO `b2b_permissions` (`id`, `name`) VALUES ('1', 'user.read.any');
INSERT INTO `b2b_permissions` (`id`, `name`) VALUES ('2', 'user.write.any');
INSERT INTO `b2b_permissions` (`id`, `name`) VALUES ('3', 'user.delete.any');
INSERT INTO `b2b_permissions` (`id`, `name`) VALUES ('4', 'currency.write');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '1');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '2');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '3');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('2', '4');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '1');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '2');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '3');
INSERT INTO `b2b_role_permissions` (`role_id`, `permission_id`) VALUES ('3', '4');
-- +goose Down

View File

@@ -0,0 +1,380 @@
-- +goose Up
DELIMITER //
DROP PROCEDURE IF EXISTS get_full_product
//
CREATE PROCEDURE get_full_product(
IN p_id_product INT UNSIGNED,
IN p_id_shop INT UNSIGNED,
IN p_id_lang INT UNSIGNED,
IN p_id_customer INT UNSIGNED,
IN b2b_id_country INT UNSIGNED,
IN p_quantity INT UNSIGNED
)
BEGIN
DECLARE v_tax_rate DECIMAL(10, 4) DEFAULT 0;
DECLARE p_id_currency DECIMAL(10, 4) DEFAULT 0;
SELECT
COALESCE(t.rate, 0.0000) INTO v_tax_rate
FROM
ps_tax_rule tr
INNER JOIN ps_tax t ON t.id_tax = tr.id_tax
LEFT JOIN b2b_countries ON b2b_countries.id = b2b_id_country
WHERE
tr.id_tax_rules_group = (
SELECT
ps.id_tax_rules_group
FROM
ps_product_shop ps
WHERE
ps.id_product = p_id_product
AND ps.id_shop = p_id_shop
LIMIT
1
)
AND tr.id_country = b2b_countries.ps_id_country
ORDER BY
tr.id_state DESC,
tr.zipcode_from != '' DESC,
tr.id_tax_rule DESC
LIMIT
1;
SELECT
b2b_currencies.ps_id_currency INTO p_id_currency
FROM
b2b_currencies
LEFT JOIN b2b_countries ON b2b_countries.b2b_id_currency = b2b_currencies.id
WHERE
b2b_countries.id = b2b_id_country
LIMIT 1;
/* FINAL JSON */
SELECT
JSON_OBJECT(
/* ================= PRODUCT ================= */
'id_product',
p.id_product,
'reference',
p.reference,
'name',
pl.name,
'description',
pl.description,
'short_description',
pl.description_short,
/* ================= PRICE ================= */
'price',
JSON_OBJECT(
'base',
COALESCE(ps.price * r.conversion_rate, p.price * r.conversion_rate),
'final_tax_excl',
(
CASE
WHEN bsp.id IS NOT NULL THEN
CASE
/* FIXED PRICE */
WHEN bsp.reduction_type = 'amount' THEN
(
CASE
WHEN bsp.b2b_id_currency IS NULL THEN bsp.price
ELSE bsp.price * br_bsp.conversion_rate
END
)
/* PERCENTAGE */
WHEN bsp.reduction_type = 'percentage' THEN
COALESCE(ps.price * r.conversion_rate, p.price * r.conversion_rate)
* (1 - bsp.percentage_reduction / 100)
ELSE
COALESCE(ps.price * r.conversion_rate, p.price * r.conversion_rate)
END
ELSE
COALESCE(ps.price * r.conversion_rate, p.price * r.conversion_rate)
END
),
'final_tax_incl',
(
(
CASE
WHEN bsp.id IS NOT NULL THEN
CASE
WHEN bsp.reduction_type = 'amount' THEN
(
CASE
WHEN bsp.b2b_id_currency IS NULL THEN bsp.price
ELSE bsp.price * br_bsp.conversion_rate
END
)
WHEN bsp.reduction_type = 'percentage' THEN
COALESCE(ps.price * r.conversion_rate, p.price * r.conversion_rate)
* (1 - bsp.percentage_reduction / 100)
ELSE
COALESCE(ps.price * r.conversion_rate, p.price * r.conversion_rate)
END
ELSE
COALESCE(ps.price * r.conversion_rate, p.price * r.conversion_rate)
END
) * (1 + v_tax_rate / 100)
)
),
/* ================= META ================= */
'active',
COALESCE(ps.active, p.active),
'visibility',
COALESCE(ps.visibility, p.visibility),
'manufacturer',
m.name,
'category',
cl.name,
/* ================= IMAGE ================= */
'cover_image',
JSON_OBJECT(
'id',
i.id_image,
'legend',
il.legend
),
/* ================= FEATURES ================= */
'features',
(
SELECT
JSON_ARRAYAGG(
JSON_OBJECT(
'name',
fl.name,
'value',
fvl.value
)
)
FROM
ps_feature_product fp
JOIN ps_feature_lang fl ON fl.id_feature = fp.id_feature
AND fl.id_lang = p_id_lang
JOIN ps_feature_value_lang fvl ON fvl.id_feature_value = fp.id_feature_value
AND fvl.id_lang = p_id_lang
WHERE
fp.id_product = p.id_product
),
/* ================= COMBINATIONS ================= */
'combinations',
(
SELECT
JSON_ARRAYAGG(
JSON_OBJECT(
'id_product_attribute',
pa.id_product_attribute,
'reference',
pa.reference,
'price',
JSON_OBJECT(
'impact',
COALESCE(pas.price, pa.price),
'final_tax_excl',
(
COALESCE(ps.price, p.price) + COALESCE(pas.price, pa.price)
),
'final_tax_incl',
(
(
COALESCE(ps.price, p.price) + COALESCE(pas.price, pa.price)
) * (1 + v_tax_rate / 100)
)
),
'stock',
IFNULL(sa.quantity, 0),
'default_on',
pas.default_on,
/* ATTRIBUTES JSON */
'attributes',
(
SELECT
JSON_ARRAYAGG(
JSON_OBJECT(
'group',
agl.name,
'attribute',
al.name
)
)
FROM
ps_product_attribute_combination pac
JOIN ps_attribute a ON a.id_attribute = pac.id_attribute
JOIN ps_attribute_lang al ON al.id_attribute = a.id_attribute
AND al.id_lang = p_id_lang
JOIN ps_attribute_group_lang agl ON agl.id_attribute_group = a.id_attribute_group
AND agl.id_lang = p_id_lang
WHERE
pac.id_product_attribute = pa.id_product_attribute
),
/* IMAGES */
'images',
(
SELECT
JSON_ARRAYAGG(img.id_image)
FROM
ps_product_attribute_image pai
JOIN ps_image img ON img.id_image = pai.id_image
WHERE
pai.id_product_attribute = pa.id_product_attribute
)
)
)
FROM
ps_product_attribute pa
JOIN ps_product_attribute_shop pas ON pas.id_product_attribute = pa.id_product_attribute
AND pas.id_shop = p_id_shop
LEFT JOIN ps_stock_available sa ON sa.id_product = pa.id_product
AND sa.id_product_attribute = pa.id_product_attribute
AND sa.id_shop = p_id_shop
WHERE
pa.id_product = p.id_product
)
) AS product_json
FROM
ps_product p
LEFT JOIN ps_product_shop ps ON ps.id_product = p.id_product
AND ps.id_shop = p_id_shop
LEFT JOIN ps_product_lang pl ON pl.id_product = p.id_product
AND pl.id_lang = p_id_lang
AND pl.id_shop = p_id_shop
LEFT JOIN ps_category_lang cl ON cl.id_category = COALESCE(ps.id_category_default, p.id_category_default)
AND cl.id_lang = p_id_lang
AND cl.id_shop = p_id_shop
LEFT JOIN ps_manufacturer m ON m.id_manufacturer = p.id_manufacturer
LEFT JOIN ps_image i ON i.id_product = p.id_product
AND i.cover = 1
LEFT JOIN ps_image_lang il ON il.id_image = i.id_image
AND il.id_lang = p_id_lang
/* SPECIFIC PRICE */
LEFT JOIN (
SELECT bsp.*
FROM b2b_specific_price bsp
/* RELATIONS */
LEFT JOIN b2b_specific_price_product bsp_p
ON bsp_p.b2b_specific_price_id = bsp.id
LEFT JOIN b2b_specific_price_category bsp_c
ON bsp_c.b2b_specific_price_id = bsp.id
WHERE bsp.is_active = TRUE
/* SCOPE MATCH */
AND (
/* PRODUCT */
(bsp.scope = 'product' AND bsp_p.id_product = p_id_product)
/* CATEGORY */
OR (
bsp.scope = 'category'
AND bsp_c.id_category IN (
SELECT cp.id_category
FROM ps_category_product cp
WHERE cp.id_product = p_id_product
)
)
/* SHOP (GLOBAL) */
OR (bsp.scope = 'shop')
)
/* CUSTOMER MATCH */
AND (
NOT EXISTS (
SELECT 1 FROM b2b_specific_price_customer c
WHERE c.b2b_specific_price_id = bsp.id
)
OR EXISTS (
SELECT 1 FROM b2b_specific_price_customer c
WHERE c.b2b_specific_price_id = bsp.id
AND c.b2b_id_customer = p_id_customer
)
)
/* COUNTRY MATCH */
AND (
NOT EXISTS (
SELECT 1 FROM b2b_specific_price_country ctry
WHERE ctry.b2b_specific_price_id = bsp.id
)
OR EXISTS (
SELECT 1 FROM b2b_specific_price_country ctry
WHERE ctry.b2b_specific_price_id = bsp.id
AND ctry.b2b_id_country = b2b_id_country
)
)
/* QUANTITY */
AND bsp.from_quantity <= p_quantity
/* DATE */
AND (
bsp.has_expiration_date = FALSE OR (
(bsp.valid_from IS NULL OR bsp.valid_from <= NOW())
AND (bsp.valid_till IS NULL OR bsp.valid_till >= NOW())
)
)
ORDER BY
/* 🔥 SCOPE PRIORITY */
bsp.scope = 'product' DESC,
bsp.scope = 'category' DESC,
bsp.scope = 'shop' DESC,
/* 🔥 CUSTOMER PRIORITY */
(
EXISTS (
SELECT 1 FROM b2b_specific_price_customer c
WHERE c.b2b_specific_price_id = bsp.id
AND c.b2b_id_customer = p_id_customer
)
) DESC,
/* 🔥 COUNTRY PRIORITY */
(
EXISTS (
SELECT 1 FROM b2b_specific_price_country ctry
WHERE ctry.b2b_specific_price_id = bsp.id
AND ctry.b2b_id_country = b2b_id_country
)
) DESC,
/* GLOBAL fallback (no restrictions) naturally goes last */
bsp.from_quantity DESC,
bsp.id DESC
LIMIT 1
) bsp ON 1=1
LEFT JOIN b2b_currency_rates br_bsp
ON br_bsp.b2b_id_currency = bsp.b2b_id_currency
AND br_bsp.created_at = (
SELECT MAX(created_at)
FROM b2b_currency_rates
WHERE b2b_id_currency = bsp.b2b_id_currency
)
LEFT JOIN b2b_countries ON b2b_countries.id = b2b_id_country
LEFT JOIN b2b_currencies ON b2b_currencies.id = p_id_currency
LEFT JOIN b2b_currency_rates r ON r.b2b_id_currency = b2b_currencies.id
AND r.created_at = (
SELECT
MAX(created_at)
FROM
b2b_currency_rates
WHERE
b2b_id_currency = b2b_currencies.id
)
WHERE
p.id_product = p_id_product
LIMIT
1;
END //
DELIMITER ;
-- +goose Down

View File

@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
SELECT 'up SQL query';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
SELECT 'down SQL query';
-- +goose StatementEnd