421 lines
11 KiB
SQL
421 lines
11 KiB
SQL
-- +goose Up
|
|
DELIMITER //
|
|
|
|
DROP FUNCTION IF EXISTS fn_product_price //
|
|
CREATE FUNCTION fn_product_price(
|
|
p_id_product INT UNSIGNED,
|
|
p_id_shop INT UNSIGNED,
|
|
p_id_customer INT UNSIGNED,
|
|
p_id_country INT UNSIGNED,
|
|
p_quantity INT UNSIGNED,
|
|
p_id_product_attribute INT UNSIGNED
|
|
)
|
|
RETURNS JSON
|
|
DETERMINISTIC
|
|
READS SQL DATA
|
|
BEGIN
|
|
|
|
DECLARE v_tax_rate DECIMAL(10,4) DEFAULT 0;
|
|
|
|
DECLARE v_base_raw DECIMAL(20,6);
|
|
DECLARE v_base DECIMAL(20,6);
|
|
DECLARE v_excl DECIMAL(20,6);
|
|
DECLARE v_incl DECIMAL(20,6);
|
|
|
|
DECLARE v_reduction_type VARCHAR(20);
|
|
DECLARE v_percentage DECIMAL(10,4);
|
|
DECLARE v_fixed_price DECIMAL(20,6);
|
|
DECLARE v_specific_currency_id BIGINT;
|
|
|
|
DECLARE v_has_specific INT DEFAULT 0;
|
|
|
|
-- currency
|
|
DECLARE v_target_currency BIGINT;
|
|
DECLARE v_target_rate DECIMAL(13,6) DEFAULT 1;
|
|
DECLARE v_specific_rate DECIMAL(13,6) DEFAULT 1;
|
|
|
|
SET p_id_product_attribute = NULLIF(p_id_product_attribute, 0);
|
|
|
|
-- ================= TAX =================
|
|
SELECT COALESCE(t.rate, 0)
|
|
INTO v_tax_rate
|
|
FROM ps_tax_rule tr
|
|
JOIN ps_tax t ON t.id_tax = tr.id_tax
|
|
LEFT JOIN b2b_countries c ON c.id = p_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 = c.ps_id_country
|
|
LIMIT 1;
|
|
|
|
-- ================= TARGET CURRENCY =================
|
|
SELECT c.b2b_id_currency
|
|
INTO v_target_currency
|
|
FROM b2b_countries c
|
|
WHERE c.id = p_id_country
|
|
LIMIT 1;
|
|
|
|
-- latest target rate
|
|
SELECT r.conversion_rate
|
|
INTO v_target_rate
|
|
FROM b2b_currency_rates r
|
|
WHERE r.b2b_id_currency = v_target_currency
|
|
ORDER BY r.created_at DESC
|
|
LIMIT 1;
|
|
|
|
-- ================= BASE PRICE (RAW) =================
|
|
SELECT
|
|
COALESCE(ps.price, p.price) + COALESCE(pas.price, 0)
|
|
INTO v_base_raw
|
|
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_attribute_shop pas
|
|
ON pas.id_product_attribute = p_id_product_attribute
|
|
AND pas.id_shop = p_id_shop
|
|
WHERE p.id_product = p_id_product;
|
|
|
|
-- convert base to target currency
|
|
SET v_base = v_base_raw * v_target_rate;
|
|
|
|
-- ================= RULE SELECTION =================
|
|
SELECT
|
|
1,
|
|
bsp.reduction_type,
|
|
bsp.percentage_reduction,
|
|
bsp.price,
|
|
bsp.b2b_id_currency
|
|
|
|
INTO
|
|
v_has_specific,
|
|
v_reduction_type,
|
|
v_percentage,
|
|
v_fixed_price,
|
|
v_specific_currency_id
|
|
|
|
FROM b2b_specific_price bsp
|
|
|
|
WHERE bsp.is_active = 1
|
|
AND bsp.from_quantity <= p_quantity
|
|
|
|
-- intersection rules (unchanged)
|
|
AND (
|
|
NOT EXISTS (SELECT 1 FROM b2b_specific_price_product x WHERE x.b2b_specific_price_id = bsp.id)
|
|
OR EXISTS (SELECT 1 FROM b2b_specific_price_product x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product = p_id_product)
|
|
)
|
|
|
|
AND (
|
|
NOT EXISTS (SELECT 1 FROM b2b_specific_price_product_attribute x WHERE x.b2b_specific_price_id = bsp.id)
|
|
OR EXISTS (SELECT 1 FROM b2b_specific_price_product_attribute x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product_attribute = p_id_product_attribute)
|
|
)
|
|
|
|
AND (
|
|
NOT EXISTS (SELECT 1 FROM b2b_specific_price_category x WHERE x.b2b_specific_price_id = bsp.id)
|
|
OR EXISTS (
|
|
SELECT 1 FROM b2b_specific_price_category x
|
|
JOIN ps_category_product cp ON cp.id_category = x.id_category
|
|
WHERE x.b2b_specific_price_id = bsp.id AND cp.id_product = p_id_product
|
|
)
|
|
)
|
|
|
|
AND (
|
|
NOT EXISTS (SELECT 1 FROM b2b_specific_price_customer x WHERE x.b2b_specific_price_id = bsp.id)
|
|
OR EXISTS (SELECT 1 FROM b2b_specific_price_customer x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_customer = p_id_customer)
|
|
)
|
|
|
|
AND (
|
|
NOT EXISTS (SELECT 1 FROM b2b_specific_price_country x WHERE x.b2b_specific_price_id = bsp.id)
|
|
OR EXISTS (SELECT 1 FROM b2b_specific_price_country x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_country = p_id_country)
|
|
)
|
|
|
|
ORDER BY
|
|
-- customer wins
|
|
(EXISTS (SELECT 1 FROM b2b_specific_price_customer x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_customer = p_id_customer)) DESC,
|
|
|
|
-- attribute
|
|
(EXISTS (SELECT 1 FROM b2b_specific_price_product_attribute x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product_attribute = p_id_product_attribute)) DESC,
|
|
|
|
-- product
|
|
(EXISTS (SELECT 1 FROM b2b_specific_price_product x WHERE x.b2b_specific_price_id = bsp.id AND x.id_product = p_id_product)) DESC,
|
|
|
|
-- category
|
|
(EXISTS (
|
|
SELECT 1 FROM b2b_specific_price_category x
|
|
JOIN ps_category_product cp ON cp.id_category = x.id_category
|
|
WHERE x.b2b_specific_price_id = bsp.id AND cp.id_product = p_id_product
|
|
)) DESC,
|
|
|
|
-- country
|
|
(EXISTS (SELECT 1 FROM b2b_specific_price_country x WHERE x.b2b_specific_price_id = bsp.id AND x.b2b_id_country = p_id_country)) DESC,
|
|
|
|
bsp.id DESC
|
|
|
|
LIMIT 1;
|
|
|
|
-- ================= APPLY =================
|
|
SET v_excl = v_base;
|
|
|
|
IF v_has_specific = 1 THEN
|
|
|
|
IF v_reduction_type = 'amount' THEN
|
|
|
|
-- convert specific price currency if needed
|
|
IF v_specific_currency_id IS NOT NULL AND v_specific_currency_id != v_target_currency THEN
|
|
|
|
SELECT r.conversion_rate
|
|
INTO v_specific_rate
|
|
FROM b2b_currency_rates r
|
|
WHERE r.b2b_id_currency = v_specific_currency_id
|
|
ORDER BY r.created_at DESC
|
|
LIMIT 1;
|
|
|
|
-- normalize → then convert to target
|
|
SET v_excl = (v_fixed_price / v_specific_rate) * v_target_rate;
|
|
|
|
ELSE
|
|
SET v_excl = v_fixed_price;
|
|
END IF;
|
|
|
|
ELSEIF v_reduction_type = 'percentage' THEN
|
|
SET v_excl = v_base * (1 - v_percentage / 100);
|
|
END IF;
|
|
|
|
END IF;
|
|
|
|
SET v_incl = v_excl * (1 + v_tax_rate / 100);
|
|
|
|
RETURN JSON_OBJECT(
|
|
'base', v_base,
|
|
'final_tax_excl', v_excl,
|
|
'final_tax_incl', v_incl,
|
|
'tax_rate', v_tax_rate,
|
|
'rate', v_target_rate
|
|
);
|
|
|
|
END //
|
|
|
|
DELIMITER ;
|
|
|
|
DELIMITER //
|
|
|
|
DROP PROCEDURE IF EXISTS get_product_variants //
|
|
CREATE PROCEDURE get_product_variants(
|
|
IN p_id_product INT,
|
|
IN p_id_shop INT,
|
|
IN p_id_lang INT,
|
|
IN p_id_customer INT,
|
|
IN p_id_country INT,
|
|
IN p_quantity INT
|
|
)
|
|
BEGIN
|
|
|
|
SELECT
|
|
pa.id_product_attribute,
|
|
pa.reference,
|
|
|
|
-- PRICE (computed once per row via correlated subquery)
|
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(
|
|
fn_product_price(
|
|
p_id_product,
|
|
p_id_shop,
|
|
p_id_customer,
|
|
p_id_country,
|
|
p_quantity,
|
|
pa.id_product_attribute
|
|
),
|
|
'$.base'
|
|
)) AS DECIMAL(20,6)) AS base_price,
|
|
|
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(
|
|
fn_product_price(
|
|
p_id_product,
|
|
p_id_shop,
|
|
p_id_customer,
|
|
p_id_country,
|
|
p_quantity,
|
|
pa.id_product_attribute
|
|
),
|
|
'$.final_tax_excl'
|
|
)) AS DECIMAL(20,6)) AS price_tax_excl,
|
|
|
|
CAST(JSON_UNQUOTE(JSON_EXTRACT(
|
|
fn_product_price(
|
|
p_id_product,
|
|
p_id_shop,
|
|
p_id_customer,
|
|
p_id_country,
|
|
p_quantity,
|
|
pa.id_product_attribute
|
|
),
|
|
'$.final_tax_incl'
|
|
)) AS DECIMAL(20,6)) AS price_tax_incl,
|
|
|
|
IFNULL(sa.quantity, 0) AS quantity,
|
|
|
|
(
|
|
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
|
|
) AS attributes
|
|
|
|
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;
|
|
|
|
END //
|
|
|
|
DELIMITER ;
|
|
|
|
DELIMITER //
|
|
|
|
DROP PROCEDURE IF EXISTS get_product_price //
|
|
CREATE PROCEDURE get_product_price(
|
|
IN p_id_product INT,
|
|
IN p_id_shop INT,
|
|
IN p_id_customer INT,
|
|
IN p_id_country INT,
|
|
IN p_quantity INT
|
|
)
|
|
BEGIN
|
|
|
|
SELECT fn_product_price(
|
|
p_id_product,
|
|
p_id_shop,
|
|
p_id_customer,
|
|
p_id_country,
|
|
p_quantity,
|
|
NULL
|
|
) AS price;
|
|
|
|
END //
|
|
|
|
DELIMITER ;
|
|
|
|
DELIMITER //
|
|
DROP PROCEDURE IF EXISTS get_product_base //
|
|
CREATE PROCEDURE get_product_base(
|
|
IN p_id_product INT,
|
|
IN p_id_shop INT,
|
|
IN p_id_lang INT,
|
|
IN p_id_customer INT
|
|
)
|
|
BEGIN
|
|
SELECT
|
|
p.id_product AS id, -- matches view.Product.ID
|
|
|
|
p.reference,
|
|
p.supplier_reference,
|
|
p.ean13,
|
|
p.upc,
|
|
p.isbn,
|
|
|
|
-- Price related (basic)
|
|
p.price AS base_price,
|
|
p.wholesale_price,
|
|
p.unity,
|
|
p.unit_price_ratio,
|
|
|
|
-- Stock & Availability
|
|
p.quantity,
|
|
p.minimal_quantity,
|
|
p.available_for_order,
|
|
p.available_date,
|
|
p.out_of_stock AS out_of_stock_behavior, -- 0=deny, 1=allow, 2=default
|
|
|
|
-- Flags
|
|
COALESCE(ps.on_sale, 0) AS on_sale,
|
|
COALESCE(ps.show_price, 1) AS show_price,
|
|
p.condition,
|
|
p.is_virtual,
|
|
|
|
-- Physical
|
|
p.weight,
|
|
p.width,
|
|
p.height,
|
|
p.depth,
|
|
p.additional_shipping_cost,
|
|
|
|
-- Delivery
|
|
p.additional_delivery_times AS delivery_days, -- you can adjust if needed
|
|
|
|
-- Status
|
|
COALESCE(ps.active, p.active) AS active,
|
|
COALESCE(ps.visibility, p.visibility) AS visibility,
|
|
p.indexed,
|
|
|
|
-- Other useful
|
|
p.date_add,
|
|
p.date_upd,
|
|
|
|
-- Language data
|
|
pl.name,
|
|
pl.description,
|
|
pl.description_short,
|
|
|
|
-- Relations
|
|
m.name AS manufacturer,
|
|
cl.name AS category,
|
|
|
|
p.is_oem,
|
|
EXISTS(
|
|
SELECT 1 FROM b2b_favorites f
|
|
WHERE f.user_id = p_id_customer AND f.product_id = p_id_product
|
|
) AS is_favorite,
|
|
CASE
|
|
WHEN ps.date_add >= DATE_SUB(
|
|
NOW(),
|
|
INTERVAL COALESCE(CAST(ps_configuration.value AS SIGNED), 20) DAY
|
|
) AND ps.active = 1
|
|
THEN 1
|
|
ELSE 0
|
|
END AS is_new
|
|
|
|
|
|
|
|
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_configuration
|
|
ON ps_configuration.name = PS_NB_DAYS_NEW_PRODUCT
|
|
|
|
WHERE p.id_product = p_id_product
|
|
LIMIT 1;
|
|
END //
|
|
DELIMITER ;
|
|
|
|
-- +goose Down
|