BEGIN; DROP FUNCTION IF EXISTS public.rpc_admin_save_delivery_staff(UUID, TEXT, TEXT, TEXT, SMALLINT); CREATE OR REPLACE FUNCTION public.rpc_admin_save_delivery_staff( p_id UUID DEFAULT NULL, p_uid UUID DEFAULT NULL, p_station_id UUID DEFAULT NULL, p_staff_no TEXT DEFAULT NULL, p_nickname TEXT DEFAULT NULL, p_avatar TEXT DEFAULT NULL, p_phone TEXT DEFAULT NULL, p_status SMALLINT DEFAULT 1, p_online_status TEXT DEFAULT 'resting', p_certificate_status TEXT DEFAULT 'pending', p_certificate_expire_at DATE DEFAULT NULL, p_service_area TEXT DEFAULT '', p_skills JSONB DEFAULT '[]'::jsonb ) RETURNS UUID LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ DECLARE v_id UUID; BEGIN IF NOT EXISTS ( SELECT 1 FROM public.ak_users WHERE auth_id = auth.uid() AND role = 'admin' ) THEN RAISE EXCEPTION 'Permission denied'; END IF; IF p_nickname IS NULL OR p_phone IS NULL THEN RAISE EXCEPTION 'Missing required fields: nickname or phone'; END IF; IF p_online_status NOT IN ('online', 'resting', 'busy') THEN RAISE EXCEPTION 'Invalid online_status'; END IF; IF p_certificate_status NOT IN ('valid', 'expired', 'pending') THEN RAISE EXCEPTION 'Invalid certificate_status'; END IF; IF p_station_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM public.ml_delivery_stations s WHERE s.id = p_station_id AND s.deleted_at IS NULL ) THEN RAISE EXCEPTION 'Delivery station not found'; END IF; IF p_id IS NULL THEN INSERT INTO public.ml_delivery_staff ( uid, station_id, staff_no, nickname, avatar, phone, status, online_status, certificate_status, certificate_expire_at, service_area, skills ) VALUES ( p_uid, p_station_id, NULLIF(p_staff_no, ''), p_nickname, p_avatar, p_phone, p_status, p_online_status, p_certificate_status, p_certificate_expire_at, COALESCE(p_service_area, ''), COALESCE(p_skills, '[]'::jsonb) ) RETURNING id INTO v_id; ELSE UPDATE public.ml_delivery_staff SET uid = COALESCE(p_uid, uid), station_id = p_station_id, staff_no = CASE WHEN p_staff_no IS NULL OR p_staff_no = '' THEN NULL ELSE p_staff_no END, nickname = COALESCE(p_nickname, nickname), avatar = COALESCE(p_avatar, avatar), phone = COALESCE(p_phone, phone), status = COALESCE(p_status, status), online_status = COALESCE(p_online_status, online_status), certificate_status = COALESCE(p_certificate_status, certificate_status), certificate_expire_at = p_certificate_expire_at, service_area = COALESCE(p_service_area, service_area), skills = COALESCE(p_skills, skills), updated_at = now() WHERE id = p_id AND deleted_at IS NULL RETURNING id INTO v_id; IF v_id IS NULL THEN RAISE EXCEPTION 'Delivery staff not found'; END IF; END IF; RETURN v_id; END; $$; REVOKE ALL ON FUNCTION public.rpc_admin_save_delivery_staff(UUID, UUID, UUID, TEXT, TEXT, TEXT, TEXT, SMALLINT, TEXT, TEXT, DATE, TEXT, JSONB) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.rpc_admin_save_delivery_staff(UUID, UUID, UUID, TEXT, TEXT, TEXT, TEXT, SMALLINT, TEXT, TEXT, DATE, TEXT, JSONB) TO authenticated; DROP FUNCTION IF EXISTS public.rpc_admin_get_delivery_staff_list(TEXT, SMALLINT, INTEGER, INTEGER); CREATE OR REPLACE FUNCTION public.rpc_admin_get_delivery_staff_list( p_search TEXT DEFAULT NULL, p_status SMALLINT DEFAULT NULL, p_page INTEGER DEFAULT 1, p_page_size INTEGER DEFAULT 20 ) RETURNS JSONB LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ DECLARE v_offset INTEGER := (p_page - 1) * p_page_size; v_total BIGINT; v_items JSONB; BEGIN IF NOT EXISTS ( SELECT 1 FROM public.ak_users WHERE auth_id = auth.uid() AND role = 'admin' ) THEN RAISE EXCEPTION 'Permission denied'; END IF; SELECT COUNT(*) INTO v_total FROM public.ml_delivery_staff s WHERE s.deleted_at IS NULL AND (p_status IS NULL OR s.status = p_status) AND ( p_search IS NULL OR p_search = '' OR s.nickname ILIKE '%' || p_search || '%' OR s.phone ILIKE '%' || p_search || '%' OR s.staff_no ILIKE '%' || p_search || '%' ); SELECT jsonb_agg(t) INTO v_items FROM ( SELECT s.id, s.uid, s.station_id, st.name AS station_name, s.staff_no, s.nickname, s.avatar, s.phone, s.status, s.is_active, s.online_status, s.certificate_status, s.certificate_expire_at, s.service_area, s.skills, s.created_at, s.updated_at FROM public.ml_delivery_staff s LEFT JOIN public.ml_delivery_stations st ON st.id = s.station_id AND st.deleted_at IS NULL WHERE s.deleted_at IS NULL AND (p_status IS NULL OR s.status = p_status) AND ( p_search IS NULL OR p_search = '' OR s.nickname ILIKE '%' || p_search || '%' OR s.phone ILIKE '%' || p_search || '%' OR s.staff_no ILIKE '%' || p_search || '%' ) ORDER BY s.created_at DESC LIMIT p_page_size OFFSET v_offset ) t; RETURN jsonb_build_object( 'total', v_total, 'items', COALESCE(v_items, '[]'::jsonb) ); END; $$; REVOKE ALL ON FUNCTION public.rpc_admin_get_delivery_staff_list(TEXT, SMALLINT, INTEGER, INTEGER) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.rpc_admin_get_delivery_staff_list(TEXT, SMALLINT, INTEGER, INTEGER) TO authenticated; DROP FUNCTION IF EXISTS public.rpc_homecare_dispatch_candidate(TEXT, UUID); CREATE OR REPLACE FUNCTION public.rpc_homecare_dispatch_candidate( p_service_code TEXT DEFAULT NULL, p_station_id UUID DEFAULT NULL ) RETURNS JSONB LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ DECLARE v_candidate JSONB; BEGIN IF auth.uid() IS NULL OR NOT EXISTS ( SELECT 1 FROM public.ak_users u WHERE u.auth_id = auth.uid() ) THEN RAISE EXCEPTION 'Permission denied'; END IF; SELECT jsonb_build_object( 'id', s.id, 'uid', s.uid, 'station_id', s.station_id, 'status', s.status, 'online_status', s.online_status, 'updated_at', s.updated_at, 'created_at', s.created_at ) INTO v_candidate FROM public.ml_delivery_staff s WHERE s.deleted_at IS NULL AND s.status = 1 AND COALESCE(s.is_active, TRUE) = TRUE AND s.online_status = 'online' AND s.uid IS NOT NULL AND (p_station_id IS NULL OR s.station_id = p_station_id) ORDER BY COALESCE(s.updated_at, s.created_at) DESC, s.created_at DESC LIMIT 1; RETURN v_candidate; END; $$; REVOKE ALL ON FUNCTION public.rpc_homecare_dispatch_candidate(TEXT, UUID) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.rpc_homecare_dispatch_candidate(TEXT, UUID) TO authenticated; COMMIT;