-- Unify asset categories onto item_categories / item_subcategories -- Run against PostgreSQL before deploying BE that expects item_* FKs on assets. -- -- Pre-check (optional): -- SELECT ac.code, ac.name AS asset_cat, ic.name AS item_cat -- FROM asset_categories ac -- JOIN item_categories ic ON UPPER(ac.code) = UPPER(ic.code) -- AND ac.deleted_at IS NULL AND ic.deleted_at IS NULL; BEGIN; -- ========================================================= -- 1) Extend item_categories with asset defaults -- ========================================================= ALTER TABLE item_categories ADD COLUMN IF NOT EXISTS code_prefix VARCHAR(10), ADD COLUMN IF NOT EXISTS default_useful_life_years INT, ADD COLUMN IF NOT EXISTS default_depreciation_method VARCHAR(20); DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'item_categories_code_prefix_key' ) THEN ALTER TABLE item_categories ADD CONSTRAINT item_categories_code_prefix_key UNIQUE (code_prefix); END IF; END $$; -- ========================================================= -- 2) Mapping tables -- ========================================================= CREATE TEMP TABLE map_asset_category ( old_asset_category_id BIGINT PRIMARY KEY, new_item_category_id BIGINT NOT NULL ); CREATE TEMP TABLE map_asset_subcategory ( old_asset_subcategory_id BIGINT PRIMARY KEY, new_item_subcategory_id BIGINT NOT NULL ); -- ========================================================= -- 3) Upsert asset_categories -> item_categories -- ========================================================= WITH src AS ( SELECT * FROM asset_categories WHERE deleted_at IS NULL ), matched AS ( SELECT s.id AS old_id, ic.id AS new_id FROM src s JOIN item_categories ic ON UPPER(ic.code) = UPPER(s.code) AND ic.deleted_at IS NULL ), inserted AS ( INSERT INTO item_categories ( code, name, code_prefix, default_useful_life_years, default_depreciation_method, is_active, created_by, updated_by, created_at, updated_at ) SELECT s.code, s.name, s.code_prefix, s.default_useful_life_years, s.default_depreciation_method, s.is_active, s.created_by, s.updated_by, s.created_at, s.updated_at FROM src s WHERE NOT EXISTS ( SELECT 1 FROM matched m WHERE m.old_id = s.id ) RETURNING id, code ) INSERT INTO map_asset_category (old_asset_category_id, new_item_category_id) SELECT old_id, new_id FROM matched UNION ALL SELECT s.id, i.id FROM src s JOIN inserted i ON UPPER(i.code) = UPPER(s.code); UPDATE item_categories ic SET code_prefix = COALESCE(ic.code_prefix, ac.code_prefix), default_useful_life_years = COALESCE(ic.default_useful_life_years, ac.default_useful_life_years), default_depreciation_method = COALESCE(ic.default_depreciation_method, ac.default_depreciation_method), updated_at = NOW() FROM asset_categories ac JOIN map_asset_category m ON m.old_asset_category_id = ac.id WHERE ic.id = m.new_item_category_id AND ac.deleted_at IS NULL; -- ========================================================= -- 4) Upsert asset_subcategories -> item_subcategories -- ========================================================= WITH src AS ( SELECT asub.*, m.new_item_category_id FROM asset_subcategories asub JOIN map_asset_category m ON m.old_asset_category_id = asub.asset_category_id WHERE asub.deleted_at IS NULL ), matched AS ( SELECT s.id AS old_id, isub.id AS new_id FROM src s JOIN item_subcategories isub ON UPPER(isub.code) = UPPER(s.code) AND isub.deleted_at IS NULL ), inserted AS ( INSERT INTO item_subcategories ( item_category_id, code, name, is_active, created_by, updated_by, created_at, updated_at ) SELECT s.new_item_category_id, s.code, s.name, s.is_active, s.created_by, s.updated_by, s.created_at, s.updated_at FROM src s WHERE NOT EXISTS ( SELECT 1 FROM matched m WHERE m.old_id = s.id ) RETURNING id, code ) INSERT INTO map_asset_subcategory (old_asset_subcategory_id, new_item_subcategory_id) SELECT old_id, new_id FROM matched UNION ALL SELECT s.id, i.id FROM src s JOIN inserted i ON UPPER(i.code) = UPPER(s.code); UPDATE item_subcategories isub SET item_category_id = mcat.new_item_category_id, updated_at = NOW() FROM map_asset_subcategory msub JOIN asset_subcategories asub ON asub.id = msub.old_asset_subcategory_id JOIN map_asset_category mcat ON mcat.old_asset_category_id = asub.asset_category_id WHERE isub.id = msub.new_item_subcategory_id; -- ========================================================= -- 5) Remap assets to item_* columns -- ========================================================= ALTER TABLE assets ADD COLUMN IF NOT EXISTS item_category_id BIGINT, ADD COLUMN IF NOT EXISTS item_subcategory_id BIGINT; UPDATE assets a SET item_category_id = mapped.new_item_category_id, item_subcategory_id = mapped.new_item_subcategory_id, updated_at = NOW() FROM ( SELECT a2.id AS asset_id, mcat.new_item_category_id, msub.new_item_subcategory_id FROM assets a2 JOIN map_asset_category mcat ON mcat.old_asset_category_id = a2.asset_category_id LEFT JOIN map_asset_subcategory msub ON msub.old_asset_subcategory_id = a2.asset_subcategory_id ) mapped WHERE a.id = mapped.asset_id; DO $$ DECLARE missing_count BIGINT; BEGIN SELECT COUNT(*) INTO missing_count FROM assets WHERE deleted_at IS NULL AND item_category_id IS NULL; IF missing_count > 0 THEN RAISE EXCEPTION '% active assets could not be mapped to item_category_id', missing_count; END IF; END $$; ALTER TABLE assets ALTER COLUMN item_category_id SET NOT NULL; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'assets_item_category_id_fkey' ) THEN ALTER TABLE assets ADD CONSTRAINT assets_item_category_id_fkey FOREIGN KEY (item_category_id) REFERENCES item_categories (id); END IF; IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'assets_item_subcategory_id_fkey' ) THEN ALTER TABLE assets ADD CONSTRAINT assets_item_subcategory_id_fkey FOREIGN KEY (item_subcategory_id) REFERENCES item_subcategories (id); END IF; END $$; CREATE INDEX IF NOT EXISTS idx_assets_item_category_id ON assets (item_category_id); CREATE INDEX IF NOT EXISTS idx_assets_item_subcategory_id ON assets (item_subcategory_id); -- ========================================================= -- 6) Drop dependent views, then old asset category FKs / columns / tables -- ========================================================= DROP VIEW IF EXISTS v_asset_expiry_alerts CASCADE; DROP VIEW IF EXISTS v_asset_next_service CASCADE; DROP VIEW IF EXISTS v_assets CASCADE; ALTER TABLE assets DROP CONSTRAINT IF EXISTS assets_asset_category_id_fkey; ALTER TABLE assets DROP CONSTRAINT IF EXISTS assets_asset_subcategory_id_fkey; DROP INDEX IF EXISTS idx_assets_category_id; DROP INDEX IF EXISTS idx_assets_subcategory_id; ALTER TABLE assets DROP COLUMN IF EXISTS asset_category_id; ALTER TABLE assets DROP COLUMN IF EXISTS asset_subcategory_id; DROP TABLE IF EXISTS asset_subcategories; DROP TABLE IF EXISTS asset_categories; COMMIT; -- Recreate reporting views after this script: -- psql ... -f scripts/patch-assets-views.sql