-- Fix create_submission_with_items to use relational park_submission_locations -- Removes temp_location_data column reference and properly stores location data DROP FUNCTION IF EXISTS public.create_submission_with_items(uuid, text, jsonb, jsonb[]); CREATE FUNCTION public.create_submission_with_items( p_user_id uuid, p_submission_type text, p_content jsonb, p_items jsonb[] ) RETURNS uuid LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' AS $$ DECLARE v_submission_id UUID; v_item JSONB; v_item_data JSONB; v_item_type TEXT; v_action_type TEXT; v_park_submission_id UUID; v_company_submission_id UUID; v_ride_submission_id UUID; v_ride_model_submission_id UUID; v_photo_submission_id UUID; v_timeline_event_submission_id UUID; v_submission_item_id UUID; v_temp_ref_key TEXT; v_temp_ref_value TEXT; v_ref_type TEXT; v_ref_order_index INTEGER; v_existing_user_id TEXT; v_existing_submission_id TEXT; v_temp_location JSONB; BEGIN -- DEFENSIVE CHECK: Warn if variables are already set BEGIN v_existing_user_id := current_setting('app.current_user_id', true); v_existing_submission_id := current_setting('app.submission_id', true); IF v_existing_user_id IS NOT NULL AND v_existing_user_id != '' THEN RAISE WARNING 'Session variable app.current_user_id already set to: % (expected clean state)', v_existing_user_id; END IF; IF v_existing_submission_id IS NOT NULL AND v_existing_submission_id != '' THEN RAISE WARNING 'Session variable app.submission_id already set to: % (expected clean state)', v_existing_submission_id; END IF; EXCEPTION WHEN OTHERS THEN NULL; END; PERFORM set_config('app.current_user_id', p_user_id::text, true); PERFORM set_config('app.submission_id', '', true); INSERT INTO content_submissions (user_id, submission_type, status, approval_mode) VALUES (p_user_id, p_submission_type, 'pending', 'full') RETURNING id INTO v_submission_id; PERFORM set_config('app.submission_id', v_submission_id::text, true); IF array_length(p_items, 1) IS NULL OR array_length(p_items, 1) = 0 THEN RAISE EXCEPTION 'Cannot create submission without items'; END IF; FOREACH v_item IN ARRAY p_items LOOP v_item_type := (v_item->>'item_type')::TEXT; v_action_type := (v_item->>'action_type')::TEXT; v_item_data := v_item->'item_data'; v_park_submission_id := NULL; v_company_submission_id := NULL; v_ride_submission_id := NULL; v_ride_model_submission_id := NULL; v_photo_submission_id := NULL; v_timeline_event_submission_id := NULL; v_temp_location := NULL; IF v_item_type = 'park' THEN v_temp_location := v_item_data->'_temp_location'; INSERT INTO park_submissions ( submission_id, name, slug, description, park_type, status, opening_date, opening_date_precision, closing_date, closing_date_precision, location_id, operator_id, property_owner_id, website_url, phone, email, banner_image_url, banner_image_id, card_image_url, card_image_id ) VALUES ( v_submission_id, v_item_data->>'name', v_item_data->>'slug', v_item_data->>'description', v_item_data->>'park_type', v_item_data->>'status', (v_item_data->>'opening_date')::DATE, v_item_data->>'opening_date_precision', (v_item_data->>'closing_date')::DATE, v_item_data->>'closing_date_precision', (v_item_data->>'location_id')::UUID, (v_item_data->>'operator_id')::UUID, (v_item_data->>'property_owner_id')::UUID, v_item_data->>'website_url', v_item_data->>'phone', v_item_data->>'email', v_item_data->>'banner_image_url', v_item_data->>'banner_image_id', v_item_data->>'card_image_url', v_item_data->>'card_image_id' ) RETURNING id INTO v_park_submission_id; IF v_temp_location IS NOT NULL AND jsonb_typeof(v_temp_location) = 'object' THEN INSERT INTO park_submission_locations ( park_submission_id, name, street_address, city, state_province, country, postal_code, latitude, longitude, timezone, display_name ) VALUES ( v_park_submission_id, v_temp_location->>'name', v_temp_location->>'street_address', v_temp_location->>'city', v_temp_location->>'state_province', v_temp_location->>'country', v_temp_location->>'postal_code', (v_temp_location->>'latitude')::NUMERIC, (v_temp_location->>'longitude')::NUMERIC, v_temp_location->>'timezone', v_temp_location->>'display_name' ); RAISE NOTICE 'Inserted location for park_submission_id=%: %', v_park_submission_id, v_temp_location->>'name'; END IF; ELSIF v_item_type IN ('manufacturer', 'operator', 'property_owner', 'designer') THEN INSERT INTO company_submissions ( submission_id, name, slug, description, company_type, person_type, founded_year, founded_date, founded_date_precision, headquarters_location, website_url, logo_url, banner_image_url, banner_image_id, card_image_url, card_image_id ) VALUES ( v_submission_id, v_item_data->>'name', v_item_data->>'slug', v_item_data->>'description', v_item_type, COALESCE(v_item_data->>'person_type', 'company'), (v_item_data->>'founded_year')::INTEGER, (v_item_data->>'founded_date')::DATE, v_item_data->>'founded_date_precision', v_item_data->>'headquarters_location', v_item_data->>'website_url', v_item_data->>'logo_url', v_item_data->>'banner_image_url', v_item_data->>'banner_image_id', v_item_data->>'card_image_url', v_item_data->>'card_image_id' ) RETURNING id INTO v_company_submission_id; ELSIF v_item_type = 'ride' THEN INSERT INTO ride_submissions ( submission_id, name, slug, description, category, status, park_id, manufacturer_id, designer_id, ride_model_id, opening_date, opening_date_precision, closing_date, closing_date_precision, height_requirement_cm, age_requirement, max_speed_kmh, duration_seconds, capacity_per_hour, gforce_max, inversions_count, length_meters, height_meters, drop_meters, banner_image_url, banner_image_id, card_image_url, card_image_id, image_url, ride_sub_type, coaster_type, seating_type, intensity_level ) VALUES ( v_submission_id, v_item_data->>'name', v_item_data->>'slug', v_item_data->>'description', v_item_data->>'category', v_item_data->>'status', (v_item_data->>'park_id')::UUID, (v_item_data->>'manufacturer_id')::UUID, (v_item_data->>'designer_id')::UUID, (v_item_data->>'ride_model_id')::UUID, (v_item_data->>'opening_date')::DATE, v_item_data->>'opening_date_precision', (v_item_data->>'closing_date')::DATE, v_item_data->>'closing_date_precision', (v_item_data->>'height_requirement_cm')::NUMERIC, (v_item_data->>'age_requirement')::INTEGER, (v_item_data->>'max_speed_kmh')::NUMERIC, (v_item_data->>'duration_seconds')::INTEGER, (v_item_data->>'capacity_per_hour')::INTEGER, (v_item_data->>'gforce_max')::NUMERIC, (v_item_data->>'inversions_count')::INTEGER, (v_item_data->>'length_meters')::NUMERIC, (v_item_data->>'height_meters')::NUMERIC, (v_item_data->>'drop_meters')::NUMERIC, v_item_data->>'banner_image_url', v_item_data->>'banner_image_id', v_item_data->>'card_image_url', v_item_data->>'card_image_id', v_item_data->>'image_url', v_item_data->>'ride_sub_type', v_item_data->>'coaster_type', v_item_data->>'seating_type', v_item_data->>'intensity_level' ) RETURNING id INTO v_ride_submission_id; ELSIF v_item_type = 'ride_model' THEN INSERT INTO ride_model_submissions ( submission_id, name, slug, manufacturer_id, category, ride_type, description, banner_image_url, banner_image_id, card_image_url, card_image_id ) VALUES ( v_submission_id, v_item_data->>'name', v_item_data->>'slug', (v_item_data->>'manufacturer_id')::UUID, v_item_data->>'category', v_item_data->>'ride_type', v_item_data->>'description', v_item_data->>'banner_image_url', v_item_data->>'banner_image_id', v_item_data->>'card_image_url', v_item_data->>'card_image_id' ) RETURNING id INTO v_ride_model_submission_id; ELSIF v_item_type = 'photo' THEN INSERT INTO photo_submissions ( submission_id, entity_type, entity_id, title ) VALUES ( v_submission_id, v_item_data->>'entity_type', (v_item_data->>'entity_id')::UUID, v_item_data->>'title' ) RETURNING id INTO v_photo_submission_id; ELSIF v_item_type IN ('timeline_event', 'milestone') THEN INSERT INTO timeline_event_submissions ( submission_id, entity_type, entity_id, event_type, event_date, event_date_precision, title, description ) VALUES ( v_submission_id, v_item_data->>'entity_type', (v_item_data->>'entity_id')::UUID, v_item_data->>'event_type', (v_item_data->>'event_date')::DATE, v_item_data->>'event_date_precision', v_item_data->>'title', v_item_data->>'description' ) RETURNING id INTO v_timeline_event_submission_id; END IF; INSERT INTO submission_items ( submission_id, item_type, action_type, park_submission_id, company_submission_id, ride_submission_id, ride_model_submission_id, photo_submission_id, timeline_event_submission_id, status, order_index, depends_on ) VALUES ( v_submission_id, v_item_type, v_action_type, v_park_submission_id, v_company_submission_id, v_ride_submission_id, v_ride_model_submission_id, v_photo_submission_id, v_timeline_event_submission_id, 'pending', COALESCE((v_item->>'order_index')::INTEGER, 0), (v_item->>'depends_on')::UUID ) RETURNING id INTO v_submission_item_id; IF v_item_data IS NOT NULL AND jsonb_typeof(v_item_data) = 'object' THEN FOR v_temp_ref_key, v_temp_ref_value IN SELECT key, value::text FROM jsonb_each_text(v_item_data) WHERE key LIKE '_temp_%_ref' AND value ~ '^\d+$' LOOP BEGIN v_ref_type := substring(v_temp_ref_key from '_temp_(.+)_ref'); v_ref_order_index := v_temp_ref_value::INTEGER; INSERT INTO submission_item_temp_refs ( submission_item_id, ref_type, ref_order_index ) VALUES ( v_submission_item_id, v_ref_type, v_ref_order_index ); RAISE NOTICE 'Stored temp ref: item_id=%, ref_type=%, order_index=%', v_submission_item_id, v_ref_type, v_ref_order_index; EXCEPTION WHEN OTHERS THEN RAISE WARNING 'Failed to store temp ref % for item %: %', v_temp_ref_key, v_submission_item_id, SQLERRM; END; END LOOP; END IF; END LOOP; PERFORM set_config('app.current_user_id', '', true); PERFORM set_config('app.submission_id', '', true); RETURN v_submission_id; EXCEPTION WHEN OTHERS THEN PERFORM set_config('app.current_user_id', '', true); PERFORM set_config('app.submission_id', '', true); RAISE NOTICE 'Submission creation failed for user % (type=%): %', p_user_id, p_submission_type, SQLERRM; RAISE; END; $$; COMMENT ON FUNCTION public.create_submission_with_items(uuid, text, jsonb, jsonb[]) IS 'Creates submission with items. Uses relational park_submission_locations table instead of temp_location_data JSONB column.';