feat: product_attribute list with prices
This commit is contained in:
@@ -377,4 +377,537 @@ LIMIT
|
||||
END //
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
|
||||
-----------------------------------
|
||||
|
||||
|
||||
DELIMITER //
|
||||
|
||||
DROP PROCEDURE IF EXISTS get_product_attributes_with_price //
|
||||
CREATE PROCEDURE get_product_attributes_with_price(
|
||||
IN p_id_lang INT UNSIGNED,
|
||||
IN p_id_product INT UNSIGNED,
|
||||
IN p_id_shop INT UNSIGNED,
|
||||
IN p_id_customer INT UNSIGNED,
|
||||
IN p_id_country INT UNSIGNED,
|
||||
IN p_quantity INT UNSIGNED
|
||||
)
|
||||
BEGIN
|
||||
|
||||
DECLARE v_tax_rate DECIMAL(10,4) DEFAULT 0;
|
||||
DECLARE v_currency_rate DECIMAL(10,4) DEFAULT 1;
|
||||
|
||||
-- =========================================================
|
||||
-- TAX
|
||||
-- =========================================================
|
||||
SELECT COALESCE(t.rate, 0)
|
||||
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 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;
|
||||
|
||||
-- =========================================================
|
||||
-- CURRENCY
|
||||
-- =========================================================
|
||||
SELECT COALESCE(r.conversion_rate, 1)
|
||||
INTO v_currency_rate
|
||||
FROM b2b_countries c
|
||||
LEFT JOIN b2b_currencies cur ON cur.id = c.b2b_id_currency
|
||||
LEFT JOIN b2b_currency_rates r ON r.b2b_id_currency = cur.id
|
||||
WHERE c.id = p_id_country
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- =========================================================
|
||||
-- MAIN RESULT
|
||||
-- =========================================================
|
||||
SELECT
|
||||
pa.id_product_attribute,
|
||||
pa.reference,
|
||||
|
||||
-- =====================================================
|
||||
-- BASE PRICE (product + attribute impact)
|
||||
-- =====================================================
|
||||
(
|
||||
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0))
|
||||
* v_currency_rate
|
||||
) AS base_price,
|
||||
|
||||
-- =====================================================
|
||||
-- FINAL PRICE EXCL (FULL RULE ENGINE)
|
||||
-- =====================================================
|
||||
COALESCE(sp.price_tax_excl,
|
||||
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0)) * v_currency_rate
|
||||
) AS price_tax_excl,
|
||||
|
||||
-- =====================================================
|
||||
-- FINAL PRICE INCL
|
||||
-- =====================================================
|
||||
(
|
||||
COALESCE(sp.price_tax_excl,
|
||||
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0)) * v_currency_rate
|
||||
)
|
||||
) * (1 + v_tax_rate / 100) AS price_tax_incl,
|
||||
|
||||
-- =====================================================
|
||||
-- STOCK
|
||||
-- =====================================================
|
||||
IFNULL(sa.quantity, 0) AS quantity,
|
||||
|
||||
-- =====================================================
|
||||
-- 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
|
||||
) 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
|
||||
|
||||
JOIN ps_product p
|
||||
ON p.id_product = pa.id_product
|
||||
|
||||
LEFT JOIN ps_product_shop ps
|
||||
ON ps.id_product = p.id_product
|
||||
AND ps.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
|
||||
|
||||
-- =====================================================
|
||||
-- FULL SPECIFIC PRICE ENGINE (IDENTICAL RULES TO MAIN)
|
||||
-- =====================================================
|
||||
LEFT JOIN (
|
||||
|
||||
SELECT *
|
||||
FROM (
|
||||
|
||||
SELECT
|
||||
pa.id_product_attribute,
|
||||
|
||||
-- FINAL PRICE
|
||||
CASE
|
||||
WHEN bsp.reduction_type = 'amount' THEN
|
||||
CASE
|
||||
WHEN bsp.b2b_id_currency IS NULL THEN bsp.price
|
||||
ELSE bsp.price
|
||||
END
|
||||
|
||||
WHEN bsp.reduction_type = 'percentage' THEN
|
||||
(
|
||||
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0))
|
||||
* v_currency_rate
|
||||
) * (1 - bsp.percentage_reduction / 100)
|
||||
|
||||
ELSE
|
||||
(
|
||||
(COALESCE(ps.price, p.price) + COALESCE(pas.price, 0))
|
||||
* v_currency_rate
|
||||
)
|
||||
END AS price_tax_excl,
|
||||
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY pa.id_product_attribute
|
||||
ORDER BY
|
||||
bsp.scope = 'product' DESC,
|
||||
bsp.scope = 'category' DESC,
|
||||
bsp.scope = 'shop' DESC,
|
||||
bsp.from_quantity DESC,
|
||||
bsp.id DESC
|
||||
) AS rn
|
||||
|
||||
FROM ps_product_attribute pa
|
||||
|
||||
JOIN ps_product p ON p.id_product = pa.id_product
|
||||
|
||||
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 = pa.id_product_attribute
|
||||
|
||||
JOIN b2b_specific_price bsp ON bsp.is_active = TRUE
|
||||
|
||||
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 pa.id_product = p_id_product
|
||||
|
||||
-- =========================
|
||||
-- SCOPE
|
||||
-- =========================
|
||||
AND (
|
||||
(bsp.scope = 'product' AND bsp_p.id_product = p_id_product)
|
||||
|
||||
OR (
|
||||
bsp.scope = 'category'
|
||||
AND bsp_c.id_category IN (
|
||||
SELECT id_category
|
||||
FROM ps_category_product
|
||||
WHERE id_product = p_id_product
|
||||
)
|
||||
)
|
||||
|
||||
OR (bsp.scope = 'shop')
|
||||
)
|
||||
|
||||
-- =========================
|
||||
-- CUSTOMER
|
||||
-- =========================
|
||||
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
|
||||
-- =========================
|
||||
AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_country c
|
||||
WHERE c.b2b_specific_price_id = bsp.id
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_country c
|
||||
WHERE c.b2b_specific_price_id = bsp.id
|
||||
AND c.b2b_id_country = p_id_country
|
||||
)
|
||||
)
|
||||
|
||||
-- =========================
|
||||
-- ATTRIBUTE
|
||||
-- =========================
|
||||
AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_product_attribute a
|
||||
WHERE a.b2b_specific_price_id = bsp.id
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_product_attribute a
|
||||
WHERE a.b2b_specific_price_id = bsp.id
|
||||
AND a.id_product_attribute = pa.id_product_attribute
|
||||
)
|
||||
)
|
||||
|
||||
-- =========================
|
||||
-- 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())
|
||||
)
|
||||
)
|
||||
|
||||
) ranked
|
||||
|
||||
WHERE ranked.rn = 1
|
||||
|
||||
) sp ON sp.id_product_attribute = pa.id_product_attribute
|
||||
|
||||
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 UNSIGNED,
|
||||
IN p_id_shop INT UNSIGNED,
|
||||
IN p_id_customer INT UNSIGNED,
|
||||
IN p_id_country INT UNSIGNED,
|
||||
IN p_quantity INT UNSIGNED
|
||||
)
|
||||
BEGIN
|
||||
|
||||
DECLARE v_tax_rate DECIMAL(10,4) DEFAULT 0;
|
||||
DECLARE v_currency_rate DECIMAL(10,4) DEFAULT 1;
|
||||
|
||||
DECLARE v_base_price DECIMAL(20,6) DEFAULT 0;
|
||||
DECLARE v_final_excl DECIMAL(20,6) DEFAULT 0;
|
||||
DECLARE v_final_incl DECIMAL(20,6) DEFAULT 0;
|
||||
|
||||
DECLARE v_has_specific INT DEFAULT 0;
|
||||
DECLARE v_reduction_type VARCHAR(20);
|
||||
DECLARE v_percentage DECIMAL(10,4);
|
||||
DECLARE v_fixed_price DECIMAL(20,6);
|
||||
DECLARE v_specific_currency_id INT;
|
||||
|
||||
-- =========================
|
||||
-- 1. TAX RATE
|
||||
-- =========================
|
||||
SELECT COALESCE(t.rate, 0)
|
||||
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 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
|
||||
ORDER BY tr.id_state DESC, tr.zipcode_from DESC, tr.id_tax_rule DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- =========================
|
||||
-- 2. CURRENCY RATE
|
||||
-- =========================
|
||||
SELECT COALESCE(r.conversion_rate, 1)
|
||||
INTO v_currency_rate
|
||||
FROM b2b_countries c
|
||||
LEFT JOIN b2b_currencies cur ON cur.id = c.b2b_id_currency
|
||||
LEFT JOIN b2b_currency_rates r ON r.b2b_id_currency = cur.id
|
||||
WHERE c.id = p_id_country
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- =========================
|
||||
-- 3. BASE PRICE
|
||||
-- =========================
|
||||
SELECT COALESCE(ps.price, p.price) * v_currency_rate
|
||||
INTO v_base_price
|
||||
FROM ps_product p
|
||||
LEFT JOIN ps_product_shop ps
|
||||
ON ps.id_product = p.id_product
|
||||
AND ps.id_shop = p_id_shop
|
||||
WHERE p.id_product = p_id_product
|
||||
LIMIT 1;
|
||||
|
||||
-- =========================
|
||||
-- 4. SPECIFIC PRICE (correct wildcard-aware match)
|
||||
-- =========================
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
LEFT JOIN b2b_specific_price_customer bsp_u
|
||||
ON bsp_u.b2b_specific_price_id = bsp.id
|
||||
|
||||
LEFT JOIN b2b_specific_price_country bsp_ct
|
||||
ON bsp_ct.b2b_specific_price_id = bsp.id
|
||||
|
||||
LEFT JOIN b2b_specific_price_product_attribute bsp_pa
|
||||
ON bsp_pa.b2b_specific_price_id = bsp.id
|
||||
|
||||
WHERE bsp.is_active = TRUE
|
||||
|
||||
-- =========================
|
||||
-- PRODUCT / CATEGORY / SHOP SCOPE
|
||||
-- =========================
|
||||
AND (
|
||||
(bsp.scope = 'product' AND bsp_p.id_product = p_id_product)
|
||||
|
||||
OR (
|
||||
bsp.scope = 'category'
|
||||
AND bsp_c.id_category IN (
|
||||
SELECT id_category
|
||||
FROM ps_category_product
|
||||
WHERE id_product = p_id_product
|
||||
)
|
||||
)
|
||||
|
||||
OR (bsp.scope = 'shop')
|
||||
)
|
||||
|
||||
-- =========================
|
||||
-- CUSTOMER RULE (wildcard-aware)
|
||||
-- =========================
|
||||
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 RULE (wildcard-aware)
|
||||
-- =========================
|
||||
AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM b2b_specific_price_country c
|
||||
WHERE c.b2b_specific_price_id = bsp.id
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM b2b_specific_price_country c
|
||||
WHERE c.b2b_specific_price_id = bsp.id
|
||||
AND c.b2b_id_country = p_id_country
|
||||
)
|
||||
)
|
||||
|
||||
-- =========================
|
||||
-- PRODUCT ATTRIBUTE RULE (wildcard-aware)
|
||||
-- =========================
|
||||
AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM b2b_specific_price_product_attribute a
|
||||
WHERE a.b2b_specific_price_id = bsp.id
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM b2b_specific_price_product_attribute a
|
||||
WHERE a.b2b_specific_price_id = bsp.id
|
||||
AND a.id_product_attribute = 0
|
||||
)
|
||||
)
|
||||
|
||||
-- =========================
|
||||
-- QUANTITY RULE
|
||||
-- =========================
|
||||
AND bsp.from_quantity <= p_quantity
|
||||
|
||||
-- =========================
|
||||
-- DATE RULE (FIXED precedence bug)
|
||||
-- =========================
|
||||
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())
|
||||
)
|
||||
)
|
||||
|
||||
-- =========================
|
||||
-- PRIORITY ORDERING (IMPROVED)
|
||||
-- =========================
|
||||
ORDER BY
|
||||
-- strongest match wins
|
||||
(EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_customer c
|
||||
WHERE c.b2b_specific_price_id = bsp.id
|
||||
)) DESC,
|
||||
|
||||
(EXISTS (
|
||||
SELECT 1 FROM b2b_specific_price_country c
|
||||
WHERE c.b2b_specific_price_id = bsp.id
|
||||
)) DESC,
|
||||
|
||||
bsp.scope = 'product' DESC,
|
||||
bsp.scope = 'category' DESC,
|
||||
bsp.scope = 'shop' DESC,
|
||||
|
||||
bsp.from_quantity DESC,
|
||||
bsp.id DESC
|
||||
|
||||
LIMIT 1;
|
||||
|
||||
-- =========================
|
||||
-- 5. APPLY SPECIFIC PRICE
|
||||
-- =========================
|
||||
SET v_final_excl = v_base_price;
|
||||
|
||||
IF v_has_specific = 1 THEN
|
||||
|
||||
IF v_reduction_type = 'amount' THEN
|
||||
|
||||
IF v_specific_currency_id IS NULL THEN
|
||||
SET v_final_excl = v_fixed_price;
|
||||
ELSE
|
||||
SET v_final_excl = v_fixed_price; -- assume already converted or pre-handled
|
||||
END IF;
|
||||
|
||||
ELSEIF v_reduction_type = 'percentage' THEN
|
||||
SET v_final_excl = v_base_price * (1 - v_percentage / 100);
|
||||
END IF;
|
||||
|
||||
END IF;
|
||||
|
||||
-- =========================
|
||||
-- 6. TAX
|
||||
-- =========================
|
||||
SET v_final_incl = v_final_excl * (1 + v_tax_rate / 100);
|
||||
|
||||
-- =========================
|
||||
-- 7. RETURN RESULT
|
||||
-- =========================
|
||||
SELECT
|
||||
p_id_product AS id_product,
|
||||
v_base_price AS price_base,
|
||||
v_final_excl AS price_tax_excl,
|
||||
v_final_incl AS price_tax_incl,
|
||||
v_tax_rate AS tax_rate;
|
||||
|
||||
END //
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
|
||||
Reference in New Issue
Block a user