\q
--
-- PostgreSQL database dump
--

-- Dumped from database version 15.13
-- Dumped by pg_dump version 15.13

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;

--
-- Name: admin; Type: SCHEMA; Schema: -; Owner: postgres
--

CREATE SCHEMA admin;


ALTER SCHEMA admin OWNER TO postgres;

--
-- Name: add_traffic(text, integer, common.dataimpulsepool); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.add_traffic(target_user_id text, traffic_gb integer, pool common.dataimpulsepool DEFAULT 'residential'::common.dataimpulsepool) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    current_role_val common.role_type;
    sub_id INT;
    balance_before TEXT;
    result JSONB;
BEGIN
    IF NOT common.is_provider() THEN
        RAISE sqlstate 'PT403' USING message = 'Access denied. Provider role required.';
    END IF;

    IF target_user_id IS NULL OR trim(target_user_id) = '' THEN
        RAISE sqlstate 'PT400' USING message = 'User ID cannot be empty';
    END IF;

    -- Check if user exists
    IF NOT EXISTS (SELECT 1 FROM legacy.all_user WHERE user_id = target_user_id) THEN
        RAISE sqlstate 'PT404' USING message = format('User %s not found', target_user_id);
    END IF;

    -- Get or create subuser for the specified pool
    SELECT u.sub_id
    FROM legacy.user u
    WHERE u.user_id = target_user_id AND u.pool = add_traffic.pool
    INTO sub_id;

    -- If subuser doesn't exist for this pool, create one
    IF sub_id IS NULL THEN
        sub_id = dataimpulse.create_subuser(
            label := 'admin_traffic_' || target_user_id,
            pool_type := pool
        )->>'id';

        INSERT INTO legacy.user (
            user_id,
            sub_id,
            pool,
            buy_num
        )
        VALUES (
            target_user_id,
            sub_id,
            pool,
            COALESCE((
                SELECT SUM(u.buy_num)
                FROM legacy.user u
                WHERE u.user_id = target_user_id
            ), 0)
        );
    END IF;

    -- Get balance before adding traffic
    SELECT dataimpulse.subuser_balance_get(sub_id)->>'balance_format'
    INTO balance_before;

    -- Add traffic
    PERFORM dataimpulse.subuser_balance_add(sub_id, traffic_gb);

    -- Create admin traffic record for audit trail
    INSERT INTO legacy.payment (
        user_id,
        subuser_id,
        paid,
        order_id,
        amount_gb,
        balance_before,
        discount,
        service,
        date
    ) VALUES (
        target_user_id,
        sub_id,
        0, -- Admin added, no payment
        'admin_' || extract(epoch from now())::text || '_' || target_user_id,
        traffic_gb,
        COALESCE(balance_before, '0 bytes'),
        '0',
        'admin_manual',
        current_date
    );

    -- Log the action
    RAISE LOG 'added % GB traffic to user % in pool %', 
        traffic_gb, target_user_id, pool;

    -- Return success response
    result := jsonb_build_object(
        'success', true,
        'message', format('Successfully added %s GB traffic to user %s', traffic_gb, target_user_id),
        'user_id', target_user_id,
        'traffic_added_gb', traffic_gb,
        'pool', pool,
        'balance_before', balance_before,
        'subuser_id', sub_id
        -- 'admin_id', common.current_user_id()
    );

    RETURN result;

EXCEPTION
    WHEN sqlstate 'PT403' THEN
        RAISE;
    WHEN sqlstate 'PT400' THEN  
        RAISE;
    WHEN sqlstate 'PT404' THEN
        RAISE;
    WHEN OTHERS THEN
        RAISE WARNING 'Error in add_traffic: % - %', SQLSTATE, SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Internal server error while adding traffic';
END $$;


ALTER FUNCTION admin.add_traffic(target_user_id text, traffic_gb integer, pool common.dataimpulsepool) OWNER TO postgres;

--
-- Name: add_translation(text, vgate.language, text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.add_translation(hash text, language_legacy_code vgate.language, content text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    PERFORM common.add_translation(hash, language_legacy_code, content);
END;
$$;


ALTER FUNCTION admin.add_translation(hash text, language_legacy_code vgate.language, content text) OWNER TO postgres;

--
-- Name: admin_stats(); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.admin_stats() RETURNS json
    LANGUAGE plpgsql
    AS $$
DECLARE
    "amount_top" json;
    "users_top" json;
BEGIN
    WITH aggregated_data AS (
        SELECT
            "amount_gb",
            count(*) AS purchases
        FROM legacy.payment
        GROUP BY "amount_gb"
        ORDER BY purchases DESC
        LIMIT 10
    )
    SELECT json_agg(
        json_build_object(
            'gb', "amount_gb",
            'purchases', purchases
        )
    )
    FROM aggregated_data
    WHERE "amount_gb" IS NOT NULL
    INTO "amount_top";

    WITH user_data AS (
        SELECT
            "user_id",
            count(*) AS purchases
        FROM legacy.payment
        GROUP BY "user_id"
        ORDER BY "purchases" DESC
        LIMIT 10
    )
    SELECT json_agg(
        json_build_object(
            'user_id', "user_id",
            'purchases', purchases
        )
    )
    FROM user_data
    WHERE "purchases" IS NOT NULl
    INTO "users_top";

    RETURN json_build_object(
        'total_users', (SELECT count(*) FROM legacy.all_user),
        'web_users', (SELECT count(*) FROM legacy.all_user WHERE NOT common.is_numeric(user_id)),
        'activated_subusers', (
            SELECT count(DISTINCT u.sub_id)
            FROM legacy.user u
            INNER JOIN legacy.payment p ON p.user_id = u.user_id
        ),
        'users_with_purchases', (
            SELECT count(DISTINCT u.user_id)
            FROM legacy.all_user u
            INNER JOIN legacy.payment p ON p.user_id = u.user_id
        ),
        'di_balance', (SELECT dataimpulse.get_user_balance()),
        'ref_balance', (SELECT SUM(ref_balance_usd) FROM legacy.all_user),
        'paid_total', (SELECT SUM(paid) FROM legacy.payment),
        'purchases_total', (SELECT COUNT(*) FROM legacy.payment),
        'amount_top', "amount_top",
        'users_top', "users_top"
    );
END
$$;


ALTER FUNCTION admin.admin_stats() OWNER TO postgres;

--
-- Name: broadcast_message(text, text[], boolean); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.broadcast_message(message text, inline_buttons text[] DEFAULT NULL::text[], users_without_purchases boolean DEFAULT false) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    target_users TEXT[];
    count INT;
BEGIN
    IF users_without_purchases THEN
        SELECT array_agg(au.user_id)
        FROM legacy.all_user au
        LEFT JOIN legacy.user u ON u.user_id = au.user_id
        WHERE u.user_id IS NULL
        INTO target_users;
    ELSE
        SELECT count(*) as count, array_agg(au.user_id)
        FROM legacy.all_user AS au
        WHERE common.is_numeric(au.user_id)
        INTO count, target_users;
    END IF;

    RAISE LOG 'target_users: %', count;

    PERFORM common.send_telegram_message(
        user_ids         := target_users,
        message_format   := (message, ARRAY[]::TEXT[]),
        inline_buttons   := inline_buttons
    );
END
$$;


ALTER FUNCTION admin.broadcast_message(message text, inline_buttons text[], users_without_purchases boolean) OWNER TO postgres;

--
-- Name: broadcast_message(text, text[], boolean, text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.broadcast_message(message text, inline_buttons text[] DEFAULT NULL::text[], users_without_purchases boolean DEFAULT false, parse_mode text DEFAULT 'MarkdownV2'::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    target_users TEXT[];
    count INT;
BEGIN
    IF users_without_purchases THEN
        SELECT array_agg(au.user_id)
        FROM legacy.all_user au
        LEFT JOIN legacy.user u ON u.user_id = au.user_id
        WHERE u.user_id IS NULL
        INTO target_users;
    ELSE
        SELECT count(*) as count, array_agg(au.user_id)
        FROM legacy.all_user AS au
        WHERE common.is_numeric(au.user_id)
        INTO count, target_users;
    END IF;

    RAISE LOG 'target_users: %', count;

    PERFORM common.send_telegram_message_2(
        user_ids         := target_users,
        message_format   := row(message, ARRAY[]::TEXT[])::common.format_promise,
        inline_buttons   := inline_buttons,
        parse_mode       := parse_mode
    );
END
$$;


ALTER FUNCTION admin.broadcast_message(message text, inline_buttons text[], users_without_purchases boolean, parse_mode text) OWNER TO postgres;

--
-- Name: calc_report_start_end(timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.calc_report_start_end(from_date timestamp with time zone DEFAULT NULL::timestamp with time zone, to_date timestamp with time zone DEFAULT NULL::timestamp with time zone) RETURNS TABLE(report_start timestamp with time zone, report_end timestamp with time zone)
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
    IF from_date IS NULL AND to_date IS NULL THEN
        report_end := now();
        report_start := date_trunc('day', report_end AT TIME ZONE 'Europe/Moscow') AT TIME ZONE 'Europe/Moscow';
        RAISE NOTICE 'AMMA IN A NULL NOW, %, %', report_start, report_end;
    ELSE
        report_end := COALESCE(to_date, now());
        report_start := COALESCE(from_date, now() - interval '24 hours');
    END IF;

    RETURN QUERY SELECT report_start, report_end;
END
$$;


ALTER FUNCTION admin.calc_report_start_end(from_date timestamp with time zone, to_date timestamp with time zone) OWNER TO postgres;

--
-- Name: change_reseller_traffic(text, integer); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.change_reseller_traffic(user_id text, traffic_amount integer) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
    INSERT INTO reseller_inner.resellers (user_id)
    VALUES (user_id)
    ON CONFLICT DO NOTHING;

    UPDATE reseller_inner.resellers
    SET traffic_amount = resellers.traffic_amount + reseller_inner.gb_to_bytes(change_reseller_traffic.traffic_amount)
    WHERE resellers.user_id = change_reseller_traffic.user_id;
END $$;


ALTER FUNCTION admin.change_reseller_traffic(user_id text, traffic_amount integer) OWNER TO postgres;

--
-- Name: create_admin_ref(text, text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.create_admin_ref(ref_code text, display_name text) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $_$
DECLARE
    target_tg_id TEXT;
    result JSONB;
BEGIN
    ref_code := trim(ref_code);
    display_name := trim(display_name);

    IF NOT common.is_provider() THEN
        RAISE sqlstate 'PT403' USING
            message = 'Only providers can create admin referral codes';
    END IF;

    IF ref_code IS NULL OR ref_code = '' THEN
        RAISE sqlstate 'PT400' USING
            message = 'Referral code cannot be empty';
    END IF;

    IF display_name IS NULL OR display_name = '' THEN
        RAISE sqlstate 'PT400' USING
            message = 'Display name cannot be empty';
    END IF;

    IF NOT (ref_code ~ '^[a-zA-Z0-9_-]+$') THEN
        RAISE sqlstate 'PT400' USING
            message = 'Referral code can only contain letters, numbers, underscores, and hyphens';
    END IF;

    INSERT INTO legacy.admin_ref (value, name, number, number_web)
    VALUES (ref_code, display_name, 0, 0) ON CONFLICT DO NOTHING;

    -- Check if the insert affected any rows
    IF NOT FOUND THEN
        RAISE sqlstate 'PT409' USING
            message = format('Referral code "%s" already exists', ref_code);
    END IF;

    RAISE LOG 'Admin % created referral code: % (name: %)', target_tg_id, ref_code, display_name;

EXCEPTION
    WHEN sqlstate 'PT403' THEN
        RAISE;
    WHEN sqlstate 'PT400' THEN  
        RAISE;
    WHEN sqlstate 'PT409' THEN
        RAISE;
    WHEN OTHERS THEN
        RAISE WARNING 'Error in create_admin_ref: % - %', SQLSTATE, SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Internal server error while creating referral code';
END $_$;


ALTER FUNCTION admin.create_admin_ref(ref_code text, display_name text) OWNER TO postgres;

--
-- Name: create_discount_promo(text, numeric, integer, boolean, text, text, boolean); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.create_discount_promo(name text, discount numeric, activations integer, is_first_purchase_only boolean, expiration_date text, allowed_user text, is_global boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF EXISTS (
        SELECT 1
        FROM legacy.disc_promocode p
        WHERE p.name = create_discount_promo.name
    ) THEN
        RAISE sqlstate 'PT409' USING
            message = 'promo with this name already exists';
    END IF;

    IF "activations" < 1 THEN
        RAISE sqlstate 'PT400' USING
            message = 'activations value must be greater than 0';
    END IF;

    IF "discount" NOT BETWEEN 0 AND 1 THEN
        RAISE sqlstate 'PT400' USING
            message = 'discount value must be from 0 to 1';
    END IF;

    IF expiration_date != 'Бессрочное действие' THEN
        BEGIN
            PERFORM TO_DATE(expiration_date, 'YYYY-MM-DD');
        EXCEPTION WHEN OTHERS THEN
            RAISE sqlstate 'PT400' USING
                message = 'Invalid expiration_date format. Expected format: YYYY-MM-DD or "Бессрочное действие"';
        END;
    END IF;

    IF allowed_user NOT IN ('Промокод для всех', 'Промокод для всех без ограничений') THEN
        IF NOT EXISTS (
            SELECT 1
            FROM legacy.all_user
            WHERE user_id = allowed_user
        ) THEN
            RAISE sqlstate 'PT404' USING
                message := 'User with this id not found';
        END IF;
    END IF;

    INSERT INTO legacy.disc_promocode(
        "name",
        "discount",
        "activations",
        "first_use",
        "term",
        "user_for",
        "is_global"
    )
    VALUES(
        create_discount_promo."name",
        create_discount_promo."discount",
        create_discount_promo."activations",
        create_discount_promo."is_first_purchase_only",
        create_discount_promo."expiration_date",
        create_discount_promo."allowed_user",
        CASE create_discount_promo."is_global" WHEN true THEN 1 ELSE 0 END
    );
END
$$;


ALTER FUNCTION admin.create_discount_promo(name text, discount numeric, activations integer, is_first_purchase_only boolean, expiration_date text, allowed_user text, is_global boolean) OWNER TO postgres;

--
-- Name: create_traffic_promo(text[], common.dataimpulsepool, integer, integer, integer, text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.create_traffic_promo(names text[], pool common.dataimpulsepool, activations integer, gb_amount integer, count integer DEFAULT 1, expiration_date text DEFAULT NULL::text) RETURNS jsonb
    LANGUAGE plpgsql
    AS $_$
DECLARE
    name TEXT;
    result JSONB := '[]';
    user_id TEXT := common.current_user_id();
BEGIN
    FOR i IN 1..count LOOP
        IF array_length(names, 1) > 0 THEN
            -- If names array is not empty, use the last element as name
            name := array_last(names);
            names := array_remove_last(names);
        ELSE 
            -- If names array is empty, generate a new name until it's unique
            LOOP
                name := common.generate_promocode_name();
                IF NOT EXISTS (
                    SELECT 1
                    FROM legacy.promocode p
                    WHERE p.promo_name = name
                ) THEN
                    EXIT;
                END IF;
            END LOOP;
        END IF;

        IF EXISTS (
            SELECT 1
            FROM legacy.promocode p
            WHERE p.promo_name = name
        ) THEN
            RAISE sqlstate 'PT409' USING
                message = 'promo with this name already exists';
        END IF;

        IF "activations" < 1 THEN
            RAISE sqlstate 'PT400' USING
                message = 'activations value must be greater than 0';
        END IF;

        IF "gb_amount" < 1 THEN
            RAISE sqlstate 'PT400' USING
                message = 'gb_amount value must be greater than 0';
        END IF;

        IF expiration_date IS NULL THEN
            BEGIN
                PERFORM TO_DATE(expiration_date, 'YYYY-MM-DD');
            EXCEPTION WHEN OTHERS THEN
                RAISE sqlstate 'PT400' USING
                    message = 'Invalid expiration_date format. Expected format: YYYY-MM-DD or "Бессрочное действие"';
            END;
        END IF;

        RAISE NOTICE 'admin.create_traffic_promo: user_id: %', user_id;

        INSERT INTO legacy.promocode(
            promo_name,
            traffic_amount,
            remaining_activation,
            term,
            pool,
            created_by
        )
        VALUES(
            "name",
            "gb_amount",
            "activations",
            "expiration_date",
            "pool",
            "user_id"
        );

        BEGIN
          PERFORM common.send_telegram_message(
            user_ids := common.admins(),
            message_format := row(common.indoc($html$
                <b>промокод %s создан пользователем %s</b>
              $html$::TEXT),
              ARRAY[
                "name",
                "user_id"
              ]::TEXT[]
            )::common.format_promise
          );
        EXCEPTION WHEN OTHERS THEN
          RAISE WARNING 'Failed to send admin notification: %', SQLERRM;
        END;

        result := result || to_jsonb(name);
    END LOOP;

    RETURN result;
END
$_$;


ALTER FUNCTION admin.create_traffic_promo(names text[], pool common.dataimpulsepool, activations integer, gb_amount integer, count integer, expiration_date text) OWNER TO postgres;

--
-- Name: daily_stats(timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.daily_stats(from_date timestamp with time zone DEFAULT NULL::timestamp with time zone, to_date timestamp with time zone DEFAULT NULL::timestamp with time zone) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    new_users_day INT;
    new_users_week INT;
    new_users_month INT;
    web_users_total INT;
    promo_gb INT;
    result JSONB = '{}';
    report_start TIMESTAMPTZ;
    report_end TIMESTAMPTZ;
    promo_gb_count INT;
    discount_promo_usage INT;
BEGIN
    SELECT * FROM admin.calc_report_start_end(from_date, to_date) INTO report_start, report_end;

    RAISE NOTICE 'from_date: %, to_date: %', from_date, to_date;
    RAISE NOTICE 'report_start: %, report_end: %', report_start, report_end;

    SELECT admin.daily_stats_slim(report_start, report_end) INTO result;

    SELECT COUNT(au)
    FROM legacy.all_user au
    WHERE au.created_at >= report_start AND au.created_at <= report_end
    INTO new_users_day;

    SELECT COUNT(au)
    FROM legacy.all_user au
    WHERE now() - au.created_at <= interval '1 week'
    INTO new_users_week;

    SELECT COUNT(au)
    FROM legacy.all_user au
    WHERE now() - au.created_at <= interval '1 month'
    INTO new_users_month;

    SELECT COUNT(au)
    FROM legacy.all_user au
    WHERE NOT common.is_numeric(au.user_id::text)
    INTO web_users_total;

    SELECT SUM(gb_amount), COUNT(cross_promo_activations)
    FROM legacy.cross_promo_activations
    WHERE created_at >= report_start AND created_at <= report_end
    INTO promo_gb, promo_gb_count;

    SELECT COUNT(p)
    FROM legacy.payment p
    WHERE p.created_at >= report_start AND p.created_at <= report_end
    AND p.discount != '0'
    INTO discount_promo_usage;

    RAISE DEBUG 'result: %', result;

    result = result || jsonb_build_object(
        'web_users_total', COALESCE(web_users_total, 0),
        'new_users_day', COALESCE(new_users_day, 0),
        'new_users_week', COALESCE(new_users_week, 0),
        'new_users_month', COALESCE(new_users_month, 0),
        'promo', jsonb_build_object(
            'traffic', jsonb_build_object(
                'gb', COALESCE(promo_gb, 0),
                'count', COALESCE(promo_gb_count, 0)
            ),
            'discount', jsonb_build_object(
                'count', COALESCE(discount_promo_usage, 0)
            )
        )
    );

    RETURN result;
END
$$;


ALTER FUNCTION admin.daily_stats(from_date timestamp with time zone, to_date timestamp with time zone) OWNER TO postgres;

--
-- Name: daily_stats_slim(timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.daily_stats_slim(from_date timestamp with time zone DEFAULT NULL::timestamp with time zone, to_date timestamp with time zone DEFAULT NULL::timestamp with time zone) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    total_count bigint;
    total_sum numeric;
    report_end timestamptz;
    report_start timestamptz;
BEGIN
    SELECT * FROM admin.calc_report_start_end(from_date, to_date) INTO report_start, report_end;
      
    RETURN admin.daily_stats_slim_direct(report_start, report_end);
END
$$;


ALTER FUNCTION admin.daily_stats_slim(from_date timestamp with time zone, to_date timestamp with time zone) OWNER TO postgres;

--
-- Name: daily_stats_slim_direct(timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.daily_stats_slim_direct(report_start timestamp with time zone DEFAULT NULL::timestamp with time zone, report_end timestamp with time zone DEFAULT NULL::timestamp with time zone) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    total_count bigint;
    total_sum numeric;
BEGIN
    SELECT COUNT(*), COALESCE(SUM(payment.paid), 0)
    INTO total_count, total_sum
    FROM legacy.payment
    FULL JOIN legacy.invoice ON payment.order_id = invoice.id -- ???/full?/dunno
    WHERE payment.created_at >= report_start
      AND payment.created_at <= report_end
      AND invoice.payment_system != 'admin_manual'
    ;

    RETURN (
        SELECT jsonb_build_object(
            'total_count', total_count,
            'total_sum', total_sum,
            'source', COALESCE(jsonb_object_agg(source, source_data), '{}'::jsonb)
        )
        FROM (
            SELECT
                source,
                jsonb_build_object(
                    'total_count', SUM(COALESCE(count, 0)),
                    'total_sum',   SUM(COALESCE(sum, 0)),
                    'pool', jsonb_object_agg(
                        pool::text,
                        jsonb_build_object(
                            'gb',    COALESCE(gb, 0),
                            'sum',   COALESCE(sum, 0),
                            'count', COALESCE(count, 0)
                        )
                    )
                ) AS source_data
            FROM (
                SELECT
                    s.source,
                    dp.pool,
                    SUM(p.amount_gb) AS gb,
                    SUM(p.paid)      AS sum,
                    COUNT(p.*)       AS count
                FROM (
                    SELECT DISTINCT source FROM legacy.invoice
                ) s
                CROSS JOIN (
                    SELECT unnest(enum_range(NULL::dataimpulsepool)) AS pool
                ) dp
                LEFT JOIN legacy.invoice i ON i.source = s.source AND i.pool = dp.pool
                LEFT JOIN legacy.payment p ON p.order_id = i.id
                    AND p.created_at >= report_start
                    AND p.created_at <= report_end
                GROUP BY s.source, dp.pool
            ) grouped
            GROUP BY source
        ) nested
    );
END
$$;


ALTER FUNCTION admin.daily_stats_slim_direct(report_start timestamp with time zone, report_end timestamp with time zone) OWNER TO postgres;

--
-- Name: delete_tariff_promo(text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.delete_tariff_promo(promo_name text) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
    DELETE FROM legacy.promocode WHERE legacy.promocode.promo_name = delete_tariff_promo.promo_name;
END;
$$;


ALTER FUNCTION admin.delete_tariff_promo(promo_name text) OWNER TO postgres;

--
-- Name: drop_balance(text, common.dataimpulsepool); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.drop_balance(user_id text, pool common.dataimpulsepool) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
    IF NOT common.is_provider() THEN
        RAISE sqlstate 'PT403' USING
            message = 'Only providers can drop balance';
    END IF;

    PERFORM dataimpulse.subuser_balance_drop(sub_id::INT)
    FROM legacy.user
    WHERE legacy.user.user_id = drop_balance.user_id
    AND legacy.user.pool = drop_balance.pool;
END $$;


ALTER FUNCTION admin.drop_balance(user_id text, pool common.dataimpulsepool) OWNER TO postgres;

--
-- Name: get_detailed_stats(integer, common.period, integer, integer, common.datetime_aggregate); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.get_detailed_stats(subuser_id integer, period common.period DEFAULT 'week'::common.period, "limit" integer DEFAULT 100, "offset" integer DEFAULT 0, datetime_aggregate common.datetime_aggregate DEFAULT 'minute'::common.datetime_aggregate) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
  RETURN dataimpulse_wrapper."sub-user.usage-stat.detail.all"(
    subuser_id,
    period,
    "limit",
    "offset",
    datetime_aggregate
  );
END $$;


ALTER FUNCTION admin.get_detailed_stats(subuser_id integer, period common.period, "limit" integer, "offset" integer, datetime_aggregate common.datetime_aggregate) OWNER TO postgres;

--
-- Name: get_detailed_stats(text, common.dataimpulsepool, common.period, integer, integer, common.datetime_aggregate); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.get_detailed_stats(user_id text, pool common.dataimpulsepool, period common.period DEFAULT 'week'::common.period, "limit" integer DEFAULT 100, "offset" integer DEFAULT 0, datetime_aggregate common.datetime_aggregate DEFAULT 'minute'::common.datetime_aggregate) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    subuser_id integer;
BEGIN
  SELECT legacy.user.sub_id::integer 
  FROM legacy.user 
  WHERE 
    legacy.user.user_id = get_detailed_stats.user_id 
    AND legacy.user.pool = get_detailed_stats.pool 
  INTO subuser_id;

  IF subuser_id IS NULL THEN
    RAISE sqlstate 'PT404' USING
        message = format('Pool %s not found for user %s', pool, user_id);
  END IF;

  RETURN dataimpulse_wrapper."sub-user.usage-stat.detail.all"(
    subuser_id,
    period,
    "limit",
    "offset",
    datetime_aggregate
  );
END $$;


ALTER FUNCTION admin.get_detailed_stats(user_id text, pool common.dataimpulsepool, period common.period, "limit" integer, "offset" integer, datetime_aggregate common.datetime_aggregate) OWNER TO postgres;

--
-- Name: get_ref_link_details(text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.get_ref_link_details(ref_code text DEFAULT NULL::text) RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER
    AS $_$
DECLARE
  result jsonb;
BEGIN
  IF NOT common.is_provider() THEN
    RAISE sqlstate 'PT403' USING message = 'Access denied. Provider role required.';
  END IF;

  IF ref_code IS NULL THEN
    RAISE sqlstate 'PT400' USING message = 'ref_code is required';
  END IF;

  WITH
  subusers AS (
    SELECT
      (elem->>'id')::text AS id,
      elem::jsonb            AS data
    FROM json_array_elements(dataimpulse.all_subusers()) AS elem
  ),
  ref_users AS (
    SELECT
      r.value      AS ref_code,
      r.name       AS display_name,
      r.created_at AS ref_created_at,
      aru.user_id,
      aru.source
    FROM legacy.admin_ref r
    LEFT JOIN legacy.admin_ref_users aru
      ON aru.admin_ref_value = r.value
    WHERE get_ref_link_details.ref_code IS NULL OR r.value = get_ref_link_details.ref_code
  ),
  user_traffic AS (
    SELECT
      u.user_id,
      COALESCE(
        jsonb_object_agg(
          u.pool,
          jsonb_build_object(
            'sub_id',        u.sub_id,
            'balance_format', COALESCE(s.data->>'balance_format', '0 bytes')
          )
        ) FILTER (WHERE u.pool IS NOT NULL),
        '{}'::jsonb
      ) AS traffic_pools,
      SUM(
        CASE
          WHEN (s.data->>'balance_gb') IS NOT NULL
            THEN (s.data->>'balance_gb')::numeric
          WHEN (s.data->>'balance') ~ '^\d+(\.\d+)?$'
            THEN (s.data->>'balance')::numeric
          ELSE 0
        END
      ) AS total_traffic_gb
    FROM legacy."user" u
    LEFT JOIN subusers s
      ON s.id = u.sub_id::text
    GROUP BY u.user_id
  ),
  stats_by_ref AS (
    SELECT
      ru.ref_code,
      COUNT(ru.user_id)                                           AS total_users,
      COUNT(ru.user_id) FILTER (WHERE lu.user_id IS NOT NULL)     AS paid_users,
      COALESCE(SUM(ut.total_traffic_gb), 0)                       AS total_traffic_gb,
      jsonb_agg(
        jsonb_build_object(
          'user_id',       ru.user_id,
          'source',        ru.source,
          'has_paid',      (lu.user_id IS NOT NULL),
          'traffic_pools', COALESCE(ut.traffic_pools, '{}'::jsonb)
        )
      )                                                           AS users
    FROM ref_users ru
    LEFT JOIN legacy."user" lu
      ON lu.user_id = ru.user_id
    LEFT JOIN user_traffic ut
      ON ut.user_id = ru.user_id
    GROUP BY ru.ref_code
  )
  SELECT
    jsonb_agg(
      jsonb_build_object(
        'ref_code',     r.value,
        'display_name', r.name,
        'created_at',   r.created_at,
        'stats', jsonb_build_object(
          'total_users',      COALESCE(s.total_users, 0),
          'paid_users',       COALESCE(s.paid_users, 0),
          'conversion_rate',
            CASE
              WHEN COALESCE(s.total_users, 0) > 0
                THEN ROUND((s.paid_users::numeric / s.total_users::numeric) * 100, 2)
              ELSE 0
            END,
          'total_traffic_gb', COALESCE(s.total_traffic_gb, 0)
        ),
        'users', COALESCE(s.users, '[]'::jsonb)
      ) ORDER BY r.created_at DESC
    )
  INTO result
  FROM legacy.admin_ref r
  LEFT JOIN stats_by_ref s
    ON s.ref_code = r.value
  WHERE get_ref_link_details.ref_code IS NULL OR r.value = get_ref_link_details.ref_code;

  IF result->0 IS NULL THEN
    RAISE sqlstate 'PT404' USING message = format('Referral link %s not found', get_ref_link_details.ref_code);
  END IF;

  RETURN result->0;
  
EXCEPTION
  WHEN sqlstate 'PT400' THEN
    RAISE;
  WHEN sqlstate 'PT404' THEN
    RAISE;
  WHEN sqlstate 'PT403' THEN
    RAISE;
  WHEN OTHERS THEN
    RAISE WARNING 'get_ref_link_details error: % — %', SQLSTATE, SQLERRM;
    RAISE sqlstate 'PT500' USING message = 'Internal server error fetching referral details';
END;
$_$;


ALTER FUNCTION admin.get_ref_link_details(ref_code text) OWNER TO postgres;

--
-- Name: get_referral_percent(); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.get_referral_percent() RETURNS double precision
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
    RETURN (SELECT value FROM legacy.system
    WHERE key = 'ref_percent');
END $$;


ALTER FUNCTION admin.get_referral_percent() OWNER TO postgres;

--
-- Name: get_setting(); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.get_setting() RETURNS TABLE(key text, value text)
    LANGUAGE plpgsql
    AS $$
BEGIN
  RETURN QUERY SELECT "key", "value" FROM "legacy"."system" WHERE "admin_access" = true;
END
$$;


ALTER FUNCTION admin.get_setting() OWNER TO postgres;

--
-- Name: get_setting(text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.get_setting(k text) RETURNS text
    LANGUAGE plpgsql
    AS $$
BEGIN
  RETURN (
    SELECT
      "value"
    FROM
      "legacy"."system"
    WHERE
      "key" = "k"
      AND "admin_access" = true
  );
END
$$;


ALTER FUNCTION admin.get_setting(k text) OWNER TO postgres;

--
-- Name: process_withdraw_request(bigint, common.withdraw_request_status, boolean); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.process_withdraw_request(request_id bigint, new_status common.withdraw_request_status, force boolean DEFAULT false) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $_$
DECLARE
    request_record RECORD;
    amount_usd NUMERIC;
    referral_balance NUMERIC;
BEGIN
    IF NOT common.is_provider() THEN
        RAISE sqlstate 'PT403' USING
            message = 'Access denied. Provider role required.';
    END IF;

    -- Get the withdraw request details
    SELECT id, user_id, amount, status, comment, deducted_at
    FROM legacy.request
    WHERE id = request_id
    INTO request_record;

    IF NOT FOUND THEN
        RAISE sqlstate 'PT404' USING
            message = format('Withdraw request %s not found', request_id);
    END IF;

    -- Convert amount from cents to USD
    amount_usd := request_record.amount::NUMERIC / 100.0;

    -- If approving the request, deduct from user's referral balance
    IF 
      request_record.deducted_at IS NULL
      AND new_status = 'approved' 
      AND request_record.status = 'pending'
    THEN 
        SELECT ref_balance_usd FROM legacy.all_user WHERE user_id = request_record.user_id INTO referral_balance;
        IF NOT FOUND THEN
            RAISE sqlstate 'PT404' USING
                message = format('User %s not found', request_record.user_id);
        END IF;

        -- Check if user has sufficient balance
        IF NOT force AND referral_balance < amount_usd THEN
            RAISE sqlstate 'PT409' USING
                message = format('User %s has insufficient referral balance for withdrawal of $%s', 
                    request_record.user_id, amount_usd);
        END IF;

        -- Deduct the amount from user's referral balance
        UPDATE legacy.all_user
        SET ref_balance_usd = COALESCE(ref_balance_usd, 0) - amount_usd
        WHERE user_id = request_record.user_id;

        UPDATE legacy.request
        SET deducted_at = NOW()
        WHERE id = request_id;

        RAISE LOG 'Deducted $%.2f from user %s referral balance for withdraw request %s', 
            amount_usd, request_record.user_id, request_id;
    END IF;

    -- Update the request status
    UPDATE legacy.request
    SET status = new_status::common.withdraw_request_status,
        updated_at = NOW()
    WHERE id = request_id;

    RAISE LOG 'Updated withdraw request %s status from %s to %s for user %s (amount: $%.2f)', 
        request_id, request_record.status, new_status, request_record.user_id, amount_usd;

EXCEPTION
    WHEN sqlstate 'PT403' THEN
        RAISE;
    WHEN sqlstate 'PT400' THEN  
        RAISE;
    WHEN sqlstate 'PT404' THEN
        RAISE;
    WHEN sqlstate 'PT409' THEN
        RAISE;
    WHEN OTHERS THEN
        RAISE WARNING 'Error in process_withdraw_request: % - %', SQLSTATE, SQLERRM;
        RAISE sqlstate 'PT500' USING message = 'Internal server error while processing withdraw request';
END $_$;


ALTER FUNCTION admin.process_withdraw_request(request_id bigint, new_status common.withdraw_request_status, force boolean) OWNER TO postgres;

--
-- Name: send_daily_stats(timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.send_daily_stats(from_date timestamp with time zone DEFAULT NULL::timestamp with time zone, to_date timestamp with time zone DEFAULT NULL::timestamp with time zone) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $_$
DECLARE
    stats JSONB;
    recipients TEXT[];
    target_tg_id TEXT;
    report_start TIMESTAMP with time zone;
    report_end TIMESTAMP with time zone;
BEGIN
    SELECT * FROM admin.calc_report_start_end(from_date, to_date) INTO report_start, report_end;

    SELECT admin.daily_stats(from_date, to_date) INTO stats;

    target_tg_id := common.get_header('X-Tg-Id');
    IF target_tg_id IS NULL THEN
        recipients := common.admins();
    ELSE
        recipients := ARRAY[target_tg_id];
    END IF;

    PERFORM common.send_telegram_message(
        recipients,
        row(common.indoc($html$
                📊 Статистика за период
                <code>%s</code> - <code>%s</code>
                🫂 Количество новых пользователей:
                    За день: %s
                    За неделю: %s
                    За месяц: %s
                👥 Всего пользователей на сайте: %s
                📈 Количество покупок:
                    В боте: %s
                    На сайте: %s
                    Всего: %s
                💳 Сумма покупок:
                    В боте: %s
                    На сайте: %s
                    Всего: %s
                📦 Промокоды:
                    Кол-во гигабайт: %s гб
                    Кол-во использований на трафик: %s
                    Кол-во использований на скидку: %s
                🏭 Продано:
                    Мобильных: %s гб
                    Резидентских: %s гб
                    Резидентских премиальных: %s гб
                    Серверных: %s гб
            $html$::TEXT),
            ARRAY[
                report_start AT TIME ZONE 'Europe/Moscow',
                report_end AT TIME ZONE 'Europe/Moscow',
                stats->>'new_users_day',
                stats->>'new_users_week',
                stats->>'new_users_month',
                stats->>'web_users_total',
                stats->'source'->'bot'->>'total_count',
                stats->'source'->'web'->>'total_count',
                stats->>'total_count',
                stats->'source'->'bot'->>'total_sum',
                stats->'source'->'web'->>'total_sum',
                stats->>'total_sum',
                stats->'promo'->'traffic'->>'gb',
                stats->'promo'->'traffic'->>'count',
                stats->'promo'->'discount'->>'count',
                (stats->'source'->'web'->'pool'->'mobile'->>'gb')::INT + (stats->'source'->'bot'->'pool'->'mobile'->>'gb')::INT,
                (stats->'source'->'web'->'pool'->'residential'->>'gb')::INT + (stats->'source'->'bot'->'pool'->'residential'->>'gb')::INT,
                (stats->'source'->'web'->'pool'->'residential_premium'->>'gb')::INT + (stats->'source'->'bot'->'pool'->'residential_premium'->>'gb')::INT,
                (stats->'source'->'web'->'pool'->'datacenter'->>'gb')::INT + (stats->'source'->'bot'->'pool'->'datacenter'->>'gb')::INT
            ]::TEXT[]
        )::common.format_promise
    );
END
$_$;


ALTER FUNCTION admin.send_daily_stats(from_date timestamp with time zone, to_date timestamp with time zone) OWNER TO postgres;

--
-- Name: send_message(text[], text, text[], text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.send_message(target_users text[], message text, inline_buttons text[] DEFAULT NULL::text[], parse_mode text DEFAULT 'MarkdownV2'::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    PERFORM common.send_telegram_message_2(
        user_ids         := target_users,
        message_format   := row(message, ARRAY[]::TEXT[])::common.format_promise,
        inline_buttons   := inline_buttons,
        parse_mode       := parse_mode
    );
END
$$;


ALTER FUNCTION admin.send_message(target_users text[], message text, inline_buttons text[], parse_mode text) OWNER TO postgres;

--
-- Name: set_setting(text, text); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.set_setting(k text, v text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
  UPDATE "legacy"."system" SET "value" = "v" WHERE "key" = "k" AND "admin_access" = true;
END
$$;


ALTER FUNCTION admin.set_setting(k text, v text) OWNER TO postgres;

--
-- Name: update_pool_price(common.dataimpulsepool, double precision); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.update_pool_price(pool_type common.dataimpulsepool, new_price double precision) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $_$
DECLARE
    target_tg_id TEXT;
BEGIN
    IF NOT common.is_provider() THEN
        RAISE sqlstate 'PT403' USING
            message = 'Only providers can update prices';
    END IF;

    target_tg_id := common.get_header('X-Tg-Id');
    
    -- Verify the user is an admin
    IF NOT EXISTS (
        SELECT 1 FROM legacy.all_user 
        WHERE user_id = target_tg_id 
        AND role = 'vgate_admin'
    ) THEN
        RAISE sqlstate 'PT403' USING
            message = 'Access denied: admin role required';
    END IF;

    -- Validate the new price
    IF new_price IS NULL OR new_price < 0 THEN
        RAISE sqlstate 'PT400' USING
            message = 'Invalid price: must be a positive number';
    END IF;

    -- Validate the pool type
    IF pool_type IS NULL THEN
        RAISE sqlstate 'PT400' USING
            message = 'Pool type is required';
    END IF;

    -- Update the price (assumes the record already exists)
    UPDATE legacy.price 
    SET 
        gb_cost_usd = new_price,
        gb_cost = (new_price * 100)::bigint,
        updated_at = NOW()
    WHERE pool = pool_type::TEXT;
    
    -- Check if the update affected any rows
    IF NOT FOUND THEN
        RAISE sqlstate 'PT404' USING
            message = format('Price record not found for pool %s', pool_type::TEXT);
    END IF;

    RAISE LOG 'Admin % updated price for pool % to $%', target_tg_id, pool_type, new_price;
END $_$;


ALTER FUNCTION admin.update_pool_price(pool_type common.dataimpulsepool, new_price double precision) OWNER TO postgres;

--
-- Name: update_referral_percent(double precision); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.update_referral_percent(new_percent double precision) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
    UPDATE legacy.system
    SET value = new_percent
    WHERE key = 'ref_percent';
END $$;


ALTER FUNCTION admin.update_referral_percent(new_percent double precision) OWNER TO postgres;

--
-- Name: update_user_referral_percent(text, double precision); Type: FUNCTION; Schema: admin; Owner: postgres
--

CREATE FUNCTION admin.update_user_referral_percent(user_id text, percent double precision) RETURNS void
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
BEGIN
    IF NOT common.is_provider() THEN
        RAISE sqlstate 'PT403' USING
            message = 'Access denied. Provider role required.';
    END IF;

    IF user_id IS NULL OR trim(user_id) = '' THEN
        RAISE sqlstate 'PT400' USING
            message = 'User ID cannot be empty';
    END IF;

    -- Check if user exists
    IF NOT EXISTS (SELECT 1 FROM legacy.all_user WHERE legacy.all_user.user_id = update_user_referral_percent.user_id) THEN
        RAISE sqlstate 'PT404' USING
            message = format('User %s not found', user_id);
    END IF;

    -- Validate percentage
    IF percent < 0.0 OR percent > 1.0 THEN
        RAISE sqlstate 'PT400' USING
            message = format('Invalid percentage %s. Must be between 0.0 and 1.0', percent);
    END IF;

    -- Update user-specific referral percentage
    UPDATE legacy.all_user
    SET pers_percent = percent::text
    WHERE legacy.all_user.user_id = update_user_referral_percent.user_id;

    RAISE LOG 'Admin updated user % referral percentage to %', user_id, percent;
END $$;


ALTER FUNCTION admin.update_user_referral_percent(user_id text, percent double precision) OWNER TO postgres;

--
-- Name: available_pay; Type: VIEW; Schema: admin; Owner: postgres
--

CREATE VIEW admin.available_pay AS
 SELECT available_pay.created_at,
    available_pay.updated_at,
    available_pay.name,
    available_pay.is_available,
    available_pay.description
   FROM legacy.available_pay;


ALTER TABLE admin.available_pay OWNER TO postgres;

--
-- Name: source_link; Type: VIEW; Schema: admin; Owner: postgres
--

CREATE VIEW admin.source_link AS
 SELECT r.name,
    r.value,
    jsonb_object_agg(s.source, COALESCE(sc.cnt, (0)::bigint)) AS count
   FROM ((legacy.admin_ref r
     CROSS JOIN ( SELECT DISTINCT admin_ref_users.source
           FROM legacy.admin_ref_users) s)
     LEFT JOIN ( SELECT admin_ref_users.admin_ref_value AS value,
            admin_ref_users.source,
            count(*) AS cnt
           FROM legacy.admin_ref_users
          GROUP BY admin_ref_users.admin_ref_value, admin_ref_users.source) sc ON (((sc.value = r.value) AND (sc.source = s.source))))
  GROUP BY r.name, r.value, r.created_at
  ORDER BY r.created_at DESC;


ALTER TABLE admin.source_link OWNER TO postgres;

--
-- Name: tariff_promo; Type: VIEW; Schema: admin; Owner: postgres
--

CREATE VIEW admin.tariff_promo AS
 WITH role_cte AS (
         SELECT all_user.role,
            common.get_header('X-Tg-Id'::text) AS user_id
           FROM legacy.all_user
          WHERE (all_user.user_id = common.get_header('X-Tg-Id'::text))
        )
 SELECT promocode.promo_name,
    promocode.traffic_amount,
    promocode.remaining_activation,
    promocode.term,
    promocode.pool,
    promocode.created_by
   FROM (legacy.promocode
     JOIN role_cte ON (true))
  WHERE ((role_cte.role = 'vgate_admin'::common.role_type) OR ((role_cte.role = 'vgate_support'::common.role_type) AND (promocode.created_by = role_cte.user_id)));


ALTER TABLE admin.tariff_promo OWNER TO postgres;

--
-- Name: unpaired_translations_legacy_language; Type: VIEW; Schema: admin; Owner: postgres
--

CREATE VIEW admin.unpaired_translations_legacy_language AS
 SELECT unpaired_translations.text_id AS hash,
    unpaired_translations.content,
    (
        CASE
            WHEN (unpaired_translations.language_code = 'ru'::bpchar) THEN 'rus'::text
            WHEN (unpaired_translations.language_code = 'en'::bpchar) THEN 'eng'::text
            ELSE NULL::text
        END)::vgate.language AS language_legacy_code
   FROM common.unpaired_translations() unpaired_translations(text_id, content, language_code, country_code, created_at, updated_at);


ALTER TABLE admin.unpaired_translations_legacy_language OWNER TO postgres;

--
-- Name: withdraw_requests; Type: VIEW; Schema: admin; Owner: postgres
--

CREATE VIEW admin.withdraw_requests AS
 SELECT request.created_at,
    request.updated_at,
    request.comment,
    request.amount,
    request.user_id,
    request.id,
    request.status
   FROM legacy.request;


ALTER TABLE admin.withdraw_requests OWNER TO postgres;

--
-- Name: SCHEMA admin; Type: ACL; Schema: -; Owner: postgres
--

GRANT ALL ON SCHEMA admin TO vgate_provider;


--
-- Name: FUNCTION get_detailed_stats(subuser_id integer, period common.period, "limit" integer, "offset" integer, datetime_aggregate common.datetime_aggregate); Type: ACL; Schema: admin; Owner: postgres
--

GRANT ALL ON FUNCTION admin.get_detailed_stats(subuser_id integer, period common.period, "limit" integer, "offset" integer, datetime_aggregate common.datetime_aggregate) TO vgate_provider;


--
-- Name: TABLE available_pay; Type: ACL; Schema: admin; Owner: postgres
--

GRANT ALL ON TABLE admin.available_pay TO vgate_provider;


--
-- Name: TABLE source_link; Type: ACL; Schema: admin; Owner: postgres
--

GRANT ALL ON TABLE admin.source_link TO vgate_provider;


--
-- Name: TABLE tariff_promo; Type: ACL; Schema: admin; Owner: postgres
--

GRANT SELECT ON TABLE admin.tariff_promo TO vgate_provider;


--
-- Name: TABLE unpaired_translations_legacy_language; Type: ACL; Schema: admin; Owner: postgres
--

GRANT ALL ON TABLE admin.unpaired_translations_legacy_language TO vgate_provider;


--
-- Name: TABLE withdraw_requests; Type: ACL; Schema: admin; Owner: postgres
--

GRANT SELECT ON TABLE admin.withdraw_requests TO vgate_provider;


--
-- PostgreSQL database dump complete
--

