-- Merge plants + warehouses into locations (type: plant | warehouse) BEGIN; CREATE TABLE IF NOT EXISTS locations ( id BIGSERIAL PRIMARY KEY, type VARCHAR(20) NOT NULL CHECK (type IN ('plant', 'warehouse')), code VARCHAR(30) NOT NULL, name VARCHAR(150) NOT NULL, parent_id BIGINT REFERENCES locations(id), gstin VARCHAR(15), address TEXT, city VARCHAR(100), state VARCHAR(100), pincode VARCHAR(10), phone VARCHAR(15), location VARCHAR(200), is_active BOOLEAN NOT NULL DEFAULT true, created_by BIGINT, updated_by BIGINT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS idx_locations_code_active ON locations (code) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_locations_type ON locations (type); CREATE INDEX IF NOT EXISTS idx_locations_parent_id ON locations (parent_id); -- Migrate plants (preserve ids) when legacy table exists DO $$ BEGIN IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'plants' ) THEN INSERT INTO locations ( id, type, code, name, gstin, address, city, state, pincode, phone, is_active, created_by, updated_by, created_at, updated_at, deleted_at ) OVERRIDING SYSTEM VALUE SELECT id, 'plant', code, name, gstin, address, city, state, pincode, phone, is_active, created_by, updated_by, created_at, updated_at, deleted_at FROM plants ON CONFLICT (id) DO NOTHING; END IF; END $$; -- Migrate warehouses (preserve ids; plant_id -> parent_id) DO $$ BEGIN IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'warehouses' ) THEN INSERT INTO locations ( id, type, code, name, parent_id, location, is_active, created_by, updated_by, created_at, updated_at, deleted_at ) OVERRIDING SYSTEM VALUE SELECT id, 'warehouse', code, name, plant_id, location, is_active, created_by, updated_by, created_at, updated_at, deleted_at FROM warehouses ON CONFLICT (id) DO NOTHING; END IF; END $$; SELECT setval( pg_get_serial_sequence('locations', 'id'), GREATEST(COALESCE((SELECT MAX(id) FROM locations), 1), 1) ); -- Drop FKs referencing plants / warehouses DO $$ DECLARE r RECORD; BEGIN IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'plants') THEN FOR r IN SELECT c.conname, c.conrelid::regclass AS tbl FROM pg_constraint c WHERE c.contype = 'f' AND c.confrelid = 'public.plants'::regclass LOOP EXECUTE format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %I', r.tbl, r.conname); END LOOP; END IF; IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'warehouses') THEN FOR r IN SELECT c.conname, c.conrelid::regclass AS tbl FROM pg_constraint c WHERE c.contype = 'f' AND c.confrelid = 'public.warehouses'::regclass LOOP EXECUTE format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %I', r.tbl, r.conname); END LOOP; END IF; END $$; -- Point existing FK columns to locations (idempotent) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_plant_id_fkey') THEN ALTER TABLE users ADD CONSTRAINT users_plant_id_fkey FOREIGN KEY (plant_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'assets_plant_id_fkey') THEN ALTER TABLE assets ADD CONSTRAINT assets_plant_id_fkey FOREIGN KEY (plant_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'assets_warehouse_id_fkey') THEN ALTER TABLE assets ADD CONSTRAINT assets_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'purchase_orders_plant_id_fkey') THEN ALTER TABLE purchase_orders ADD CONSTRAINT purchase_orders_plant_id_fkey FOREIGN KEY (plant_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'purchase_orders_warehouse_id_fkey') THEN ALTER TABLE purchase_orders ADD CONSTRAINT purchase_orders_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'grn_warehouse_id_fkey') THEN ALTER TABLE grn ADD CONSTRAINT grn_warehouse_id_fkey FOREIGN KEY (warehouse_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_from_plant_id_fkey') THEN ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_from_plant_id_fkey FOREIGN KEY (from_plant_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_to_plant_id_fkey') THEN ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_to_plant_id_fkey FOREIGN KEY (to_plant_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_from_warehouse_id_fkey') THEN ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_from_warehouse_id_fkey FOREIGN KEY (from_warehouse_id) REFERENCES locations(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'asset_transfers_to_warehouse_id_fkey') THEN ALTER TABLE asset_transfers ADD CONSTRAINT asset_transfers_to_warehouse_id_fkey FOREIGN KEY (to_warehouse_id) REFERENCES locations(id); END IF; END $$; -- Drop views that depend on plants/warehouses, then drop legacy tables DROP VIEW IF EXISTS v_purchase_orders CASCADE; DROP VIEW IF EXISTS v_grn CASCADE; DROP VIEW IF EXISTS v_assets CASCADE; DROP VIEW IF EXISTS v_asset_expiry_alerts CASCADE; DROP VIEW IF EXISTS v_asset_next_service CASCADE; DROP TABLE IF EXISTS warehouses; DROP TABLE IF EXISTS plants; COMMIT;