Compare commits

...
10 Commits
9 changed files with 402 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
-- Backfill any category that exists in categories_v2_lft_rgt but is missing
-- from the canonical `categories` table. The canonical table is what
-- product_category references via FK, so every v2 category must have a
-- corresponding row there (with is_v2 = true).
--
-- This covers categories 1824 and 1825 (created before insertNode was fixed)
-- and any other gaps that may exist. ON CONFLICT makes it safe to re-run.
INSERT INTO categories (id, name, is_v2)
SELECT v2.id, v2.name, true
FROM categories_v2_lft_rgt v2
WHERE NOT EXISTS (
SELECT 1 FROM categories c WHERE c.id = v2.id
)
ON CONFLICT (id) DO NOTHING;
+21
View File
@@ -0,0 +1,21 @@
-- Rename the duplicate Edgar Degas category (id 1826) that was created by mistake.
-- Renaming it will make sure we do not get collision and undefined behavior on path lookups.
UPDATE categories
SET name = 'Edgar Degas (duplicate, to be deleted)'
WHERE id = 1826;
UPDATE categories_v2_lft_rgt
SET name = 'Edgar Degas (duplicate, to be deleted)'
WHERE id = 1826;
-- For cateogry_texts_v2 update wallpaper_name, canvas_name and name since all were set on category creation
UPDATE category_texts_v2
SET wallpaper_name = 'Edgar Degas (duplicate, to be deleted)',
canvas_name = 'Edgar Degas (duplicate, to be deleted)',
name = 'Edgar Degas (duplicate, to be deleted)'
WHERE category_id = 1826;
REFRESH MATERIALIZED VIEW mv_category_locale_safexmlpath_v2;
REFRESH MATERIALIZED VIEW v_categorytree_v2;
REFRESH MATERIALIZED VIEW v_esales_categorytree_i18n_v2;
+97
View File
@@ -0,0 +1,97 @@
-- View: public.v_funnel_order_rows_fixed_discount_amount
DROP VIEW IF EXISTS public.v_funnel_order_rows_fixed_discount_amount;
CREATE VIEW public.v_funnel_order_rows_fixed_discount_amount
AS
WITH discounts AS (
SELECT s.id,
s.order_id,
s.discount,
s.discount_cc,
s.discount_code,
s.discount_sale
FROM ( SELECT rows.id,
rows.order_id,
CASE
WHEN rows.sub_type = ANY (ARRAY['contract_customer_discount'::order_row_sub_type, 'discount_code'::order_row_sub_type]) THEN 0::numeric
WHEN rows.type = 'refund'::order_row_type THEN COALESCE((rows.data ->> 'discount_amount_adjusted'::text)::numeric, 0::numeric)
ELSE 0::numeric - COALESCE((rows.data ->> 'discount_amount'::text)::numeric, 0::numeric)
END AS discount,
CASE
WHEN rows.sub_type = ANY (ARRAY['contract_customer_discount'::order_row_sub_type, 'discount_code'::order_row_sub_type]) THEN 0::numeric
WHEN (EXISTS ( SELECT 1
FROM order_rows s_1
WHERE s_1.order_id = rows.order_id AND s_1.sub_type = 'contract_customer_discount'::order_row_sub_type)) THEN
CASE
WHEN rows.type = 'refund'::order_row_type THEN COALESCE((rows.data ->> 'discount_amount_adjusted'::text)::numeric, 0::numeric)
ELSE 0::numeric - COALESCE((rows.data ->> 'discount_amount'::text)::numeric, 0::numeric)
END
ELSE 0::numeric
END AS discount_cc,
CASE
WHEN rows.sub_type = ANY (ARRAY['contract_customer_discount'::order_row_sub_type, 'discount_code'::order_row_sub_type]) THEN 0::numeric
WHEN (EXISTS ( SELECT 1
FROM order_rows s_1
WHERE s_1.order_id = rows.order_id AND s_1.sub_type = 'discount_code'::order_row_sub_type)) THEN
CASE
WHEN rows.type = 'refund'::order_row_type THEN COALESCE((rows.data ->> 'discount_amount_adjusted'::text)::numeric, 0::numeric)
ELSE 0::numeric - COALESCE((rows.data ->> 'discount_amount'::text)::numeric, 0::numeric)
END
ELSE 0::numeric
END AS discount_code,
CASE
WHEN rows.type = 'refund'::order_row_type THEN 0::numeric
ELSE 0::numeric - abs(COALESCE(( SELECT abs(rows.price) - (((rows.data -> 'applied_sale'::text) ->> 'original_unit_price_excl_vat'::text)::numeric)), 0::numeric))
END AS discount_sale
FROM order_rows rows) s
)
SELECT r.id,
r.order_id,
r.inserted,
r.type,
o.customer_type,
o.market,
pp.path,
pp.publishing_date,
pp.ref1,
pp.ref2,
COALESCE(((r.data ->> 'width_mm'::text)::numeric)::integer / 10, (r.data ->> 'width'::text)::integer) AS width,
COALESCE(((r.data ->> 'height_mm'::text)::numeric)::integer / 10, (r.data ->> 'height'::text)::integer) AS height,
r.sub_type,
r.status,
r.price,
r.commission_amount,
r.external_status,
r.data ->> 'printId'::text AS print_id,
COALESCE(r.data ->> 'paintId'::text, r.data ->> 'productId'::text) AS product_id,
r.data ->> 'material'::text AS material,
r.data ->> 'artNo'::text AS artno,
r.data ->> 'designer'::text AS designer,
(r.data ->> 'inquiryid'::text)::integer AS inquiry_id,
d.discount,
d.discount_cc,
d.discount_code,
d.discount_sale,
CASE r.type
WHEN 'refund'::order_row_type THEN NULL::text
ELSE r.data ->> 'segment'::text
END AS segment,
CASE r.type
WHEN 'refund'::order_row_type THEN NULL::numeric
ELSE (r.data ->> 'segment_price'::text)::numeric
END AS segment_price,
r.data ->> 'reprinted_order_row_id'::text AS reprinted_order_row_id,
CASE
WHEN r.sub_type = ANY (ARRAY['wall_paint'::order_row_sub_type, 'paint_card_sample'::order_row_sub_type]) THEN r.data ->> 'name'::text
ELSE NULL::text
END AS paint_name,
r.data ->> 'paintLiters'::text AS paint_liters,
r.data ->> 'paintColor'::text AS paint_ncs,
r.data ->> 'external_product_id' AS external_product_id
FROM order_rows r
JOIN orders o ON r.order_id = o.id
LEFT JOIN "product-products" pp ON ((r.data ->> 'productId'::text)::integer) = pp.productid
LEFT JOIN discounts d ON r.id = d.id;
GRANT SELECT ON TABLE public.v_funnel_order_rows_fixed_discount_amount TO funnel;
+97
View File
@@ -0,0 +1,97 @@
DROP VIEW IF EXISTS public.v_funnel_order_rows_fixed_discount_amount;
DROP VIEW IF EXISTS public.v_funnel_order_rows;
CREATE VIEW public.v_funnel_order_rows
AS
WITH discounts AS (
SELECT s.id,
s.order_id,
s.discount,
s.discount_cc,
s.discount_code,
s.discount_sale
FROM ( SELECT rows.id,
rows.order_id,
CASE
WHEN rows.sub_type = ANY (ARRAY['contract_customer_discount'::order_row_sub_type, 'discount_code'::order_row_sub_type]) THEN 0::numeric
WHEN rows.type = 'refund'::order_row_type THEN COALESCE((rows.data ->> 'discount_amount_adjusted'::text)::numeric, 0::numeric)
ELSE 0::numeric - COALESCE((rows.data ->> 'discount_amount'::text)::numeric, 0::numeric)
END AS discount,
CASE
WHEN rows.sub_type = ANY (ARRAY['contract_customer_discount'::order_row_sub_type, 'discount_code'::order_row_sub_type]) THEN 0::numeric
WHEN (EXISTS ( SELECT 1
FROM order_rows s_1
WHERE s_1.order_id = rows.order_id AND s_1.sub_type = 'contract_customer_discount'::order_row_sub_type)) THEN
CASE
WHEN rows.type = 'refund'::order_row_type THEN COALESCE((rows.data ->> 'discount_amount_adjusted'::text)::numeric, 0::numeric)
ELSE 0::numeric - COALESCE((rows.data ->> 'discount_amount'::text)::numeric, 0::numeric)
END
ELSE 0::numeric
END AS discount_cc,
CASE
WHEN rows.sub_type = ANY (ARRAY['contract_customer_discount'::order_row_sub_type, 'discount_code'::order_row_sub_type]) THEN 0::numeric
WHEN (EXISTS ( SELECT 1
FROM order_rows s_1
WHERE s_1.order_id = rows.order_id AND s_1.sub_type = 'discount_code'::order_row_sub_type)) THEN
CASE
WHEN rows.type = 'refund'::order_row_type THEN COALESCE((rows.data ->> 'discount_amount_adjusted'::text)::numeric, 0::numeric)
ELSE 0::numeric - COALESCE((rows.data ->> 'discount_amount'::text)::numeric, 0::numeric)
END
ELSE 0::numeric
END AS discount_code,
CASE
WHEN rows.type = 'refund'::order_row_type THEN 0::numeric
ELSE 0::numeric - abs(COALESCE(( SELECT abs(rows.price) - (((rows.data -> 'applied_sale'::text) ->> 'original_unit_price_excl_vat'::text)::numeric)), 0::numeric))
END AS discount_sale
FROM order_rows rows) s
)
SELECT r.id,
r.order_id,
r.inserted,
r.type,
o.customer_type,
o.market,
pp.path,
pp.publishing_date,
pp.ref1,
pp.ref2,
COALESCE(((r.data ->> 'width_mm'::text)::numeric)::integer / 10, (r.data ->> 'width'::text)::integer) AS width,
COALESCE(((r.data ->> 'height_mm'::text)::numeric)::integer / 10, (r.data ->> 'height'::text)::integer) AS height,
r.sub_type,
r.status,
r.price,
r.commission_amount,
r.external_status,
r.data ->> 'printId'::text AS print_id,
COALESCE(r.data ->> 'paintId'::text, r.data ->> 'productId'::text) AS product_id,
r.data ->> 'material'::text AS material,
r.data ->> 'artNo'::text AS artno,
r.data ->> 'designer'::text AS designer,
(r.data ->> 'inquiryid'::text)::integer AS inquiry_id,
d.discount,
d.discount_cc,
d.discount_code,
d.discount_sale,
CASE r.type
WHEN 'refund'::order_row_type THEN NULL::text
ELSE r.data ->> 'segment'::text
END AS segment,
CASE r.type
WHEN 'refund'::order_row_type THEN NULL::numeric
ELSE (r.data ->> 'segment_price'::text)::numeric
END AS segment_price,
r.data ->> 'reprinted_order_row_id'::text AS reprinted_order_row_id,
CASE
WHEN r.sub_type = ANY (ARRAY['wall_paint'::order_row_sub_type, 'paint_card_sample'::order_row_sub_type]) THEN r.data ->> 'name'::text
ELSE NULL::text
END AS paint_name,
r.data ->> 'paintLiters'::text AS paint_liters,
r.data ->> 'paintColor'::text AS paint_ncs,
r.data ->> 'external_product_id' AS external_product_id
FROM order_rows r
JOIN orders o ON r.order_id = o.id
LEFT JOIN "product-products" pp ON ((r.data ->> 'productId'::text)::integer) = pp.productid
LEFT JOIN discounts d ON r.id = d.id;
GRANT SELECT ON TABLE public.v_funnel_order_rows TO funnel;
+18
View File
@@ -0,0 +1,18 @@
-- Add matte-wallpaper material and drop legacy price column from product-materials
-- 1. Insert the new material
INSERT INTO "product-materials" (materialid, material)
VALUES (7, 'matte-wallpaper');
-- 2. Add pricing rows for all existing market × segment combinations (copy from premium-wallpaper, material_id=4)
INSERT INTO prices_wallpaper_m2 (material_id, segment_id, market_id, price, updated)
SELECT 7, segment_id, market_id, price, NOW()
FROM prices_wallpaper_m2
WHERE material_id = 4;
-- 3. Add to product-printproducts_materials for all print products that have premium-wallpaper (material 4)
INSERT INTO "product-printproducts_materials" (printid, materialid)
SELECT printid, 7
FROM "product-printproducts_materials"
WHERE materialid = 4;
+2
View File
@@ -0,0 +1,2 @@
ALTER TABLE orders
ADD COLUMN IF NOT EXISTS prioritized BOOLEAN DEFAULT NULL;
+140
View File
@@ -0,0 +1,140 @@
-- Phase 1 compatibility migration for category tree decommission.
-- Keep both legacy and _v2 object families, but make legacy objects mirror _v2 content.
-- Ensure legacy categories has support column used by migration scripts.
ALTER TABLE categories ADD COLUMN IF NOT EXISTS is_v2 BOOLEAN NOT NULL DEFAULT FALSE;
-- Sync nested-set values from categories_v2_lft_rgt into categories.
INSERT INTO categories (id, name, lft, rgt, is_v2)
SELECT
v2.id,
COALESCE(v2.name, 'unknown'),
v2.lft,
v2.rgt,
TRUE
FROM categories_v2_lft_rgt v2
ON CONFLICT (id) DO UPDATE
SET
name = EXCLUDED.name,
lft = EXCLUDED.lft,
rgt = EXCLUDED.rgt,
is_v2 = TRUE;
-- Remove legacy rows that have no counterpart in v2.
-- Must delete from dependent tables first to satisfy foreign key constraints.
DELETE FROM product_category
WHERE NOT EXISTS (
SELECT 1 FROM categories_v2_lft_rgt
WHERE categories_v2_lft_rgt.id = product_category.category_id
);
DELETE FROM category_keyword
WHERE NOT EXISTS (
SELECT 1 FROM categories_v2_lft_rgt
WHERE categories_v2_lft_rgt.id = category_keyword.category_id
);
DELETE FROM categorydata
WHERE NOT EXISTS (
SELECT 1 FROM categories_v2_lft_rgt
WHERE categories_v2_lft_rgt.id = categorydata.category_id
);
DELETE FROM category_images
WHERE NOT EXISTS (
SELECT 1 FROM categories_v2_lft_rgt
WHERE categories_v2_lft_rgt.id = category_images.category_id
);
DELETE FROM category_metadata
WHERE NOT EXISTS (
SELECT 1 FROM categories_v2_lft_rgt
WHERE categories_v2_lft_rgt.id = category_metadata.category_id
);
DELETE FROM category_texts
WHERE NOT EXISTS (
SELECT 1 FROM categories_v2_lft_rgt
WHERE categories_v2_lft_rgt.id = category_texts.category_id
);
DELETE FROM categories
WHERE NOT EXISTS (
SELECT 1 FROM categories_v2_lft_rgt
WHERE categories_v2_lft_rgt.id = categories.id
);
-- Keep sequence in sync after upsert.
SELECT setval('categories_id_seq', (SELECT COALESCE(MAX(id), 1) FROM categories), TRUE);
-- Sync category_texts from category_texts_v2 by business key (category_id, locale_id).
-- We intentionally exclude id so legacy table keeps its own sequence semantics.
-- Use delete+insert to avoid depending on a specific unique index definition.
DO $$
DECLARE
insert_columns TEXT;
BEGIN
SELECT string_agg(format('%I', c.column_name), ', ' ORDER BY c.ordinal_position)
INTO insert_columns
FROM information_schema.columns c
WHERE c.table_schema = 'public'
AND c.table_name = 'category_texts'
AND c.column_name <> 'id';
DELETE FROM category_texts t
USING category_texts_v2 v2
WHERE t.category_id = v2.category_id
AND t.locale_id = v2.locale_id;
EXECUTE format(
'INSERT INTO category_texts (%1$s) SELECT %1$s FROM category_texts_v2 WHERE category_id IN (SELECT id FROM categories)',
insert_columns
);
END $$;
-- Remove legacy category_texts rows that have no counterpart in v2.
DELETE FROM category_texts
WHERE NOT EXISTS (
SELECT 1 FROM category_texts_v2
WHERE category_texts_v2.category_id = category_texts.category_id
AND category_texts_v2.locale_id = category_texts.locale_id
);
-- Keep existing non-v2 view definitions as-is.
-- We only refresh legacy materialized views so they reflect synced base table data.
REFRESH MATERIALIZED VIEW v_categorytree;
REFRESH MATERIALIZED VIEW v_esales_categorytree_i18n;
-- Keep optional non-v2 materialized helper in sync if present.
REFRESH MATERIALIZED VIEW mv_category_locale_safexmlpath;
-- Replace legacy hidden-category dependent joins with safe ancestor join logic.
-- This is applied deterministically for the affected non-v2 views.
CREATE OR REPLACE VIEW v_product_categories AS
SELECT DISTINCT tree.id,
tree.locale_id,
getsafexmlpath(tree.path::character varying, 2) AS path,
COALESCE(category_texts.display_name::varchar(255), category_texts.name) AS name,
product_category.product_id
FROM product_category
JOIN categories ON product_category.category_id = categories.id
JOIN v_esales_categorytree_i18n_unmaterialized tree ON (
((tree.lft < categories.lft AND tree.rgt > categories.rgt) OR categories.id = tree.id)
AND tree.id <> 1
)
JOIN category_texts ON tree.id = category_texts.category_id AND tree.locale_id = category_texts.locale_id;
CREATE OR REPLACE VIEW v_analytics_product_categories AS
SELECT DISTINCT tree.id AS category_id,
getsafexmlpath(tree.path::character varying, 2) AS category_path,
category_texts.name AS category_name,
product_category.product_id,
GREATEST(product_category.inserted, category_texts.updated) AS last_updated
FROM product_category
JOIN categories ON product_category.category_id = categories.id
JOIN v_esales_categorytree_i18n tree ON (
((tree.lft < categories.lft AND tree.rgt > categories.rgt) OR categories.id = tree.id)
AND tree.id <> 1
)
JOIN category_texts ON tree.id = category_texts.category_id AND tree.locale_id = category_texts.locale_id
WHERE tree.locale_id = 3;
+11
View File
@@ -0,0 +1,11 @@
-- Align legacy ancestor view with v2 behavior by excluding root category id = 1.
CREATE OR REPLACE VIEW v_category_with_ancestors AS
SELECT c.id,
parent.id AS ancestor_id,
parent.name
FROM categories c
LEFT JOIN categories parent
ON parent.lft <= c.lft
AND parent.rgt >= c.rgt
AND parent.id <> 1
WHERE c.id <> 1;
+2
View File
@@ -0,0 +1,2 @@
ALTER TABLE ingrid_deliveries
ADD COLUMN IF NOT EXISTS pickup_location_id TEXT DEFAULT NULL;