--
-- Admin Server: демо-развёртывание (схема + справочники + StartPack + лицензия + DemoSeed)
-- Собрано из: Schema_only.sql, Directories.sql, StartPack.sql, License.sql, DemoSeed
-- DemoSeed взят из: C:\Projects\Admin Server 2\DB\DemoSeed.sql
--
-- Имя базы данных: AdminServer_Cloud (создайте пустую БД с этим именем)
--
--   createdb -h HOST -U USER AdminServer_Cloud
--   psql -h HOST -U USER -d AdminServer_Cloud -v ON_ERROR_STOP=1 -f DemoFullDeploy.sql
--
-- В Schema_only при сборке: set_config(search_path) → public (db_bundle.py).
--

--
-- PostgreSQL database dump
--

-- Dumped from database version 14.4
-- Dumped by pg_dump version 17.6

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

--
-- Name: public; Type: SCHEMA; Schema: -; Owner: -
--

-- *not* creating schema, since initdb creates it


--
-- Name: book_book_code_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.book_book_code_get(p_libraryid uuid) RETURNS TABLE(book_code text)
    LANGUAGE sql STABLE
    AS $$
    SELECT book_code
    FROM lib_books 
    WHERE date_updated IS NULL 
      AND library_id = p_libraryid;
$$;


--
-- Name: book_book_full_info_update(text, text, text, text, integer, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.book_book_full_info_update(p_bookcode text, p_bookname text, p_authorsname text, p_bookplace text, p_isbook integer, p_libraryid uuid, p_stateid integer) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE lib_books
    SET 
        book_name = CASE 
            WHEN book_name IS NULL OR book_name = '' THEN p_bookname
            ELSE book_name
        END,
        authors_name = CASE 
            WHEN authors_name IS NULL OR authors_name = '' THEN p_authorsname
            ELSE authors_name
        END,
        book_place = CASE 
            WHEN book_place IS NULL OR book_place = '' THEN p_bookplace
            ELSE book_place
        END,
        is_book = CASE 
            WHEN is_book IS NULL THEN p_isbook
            ELSE is_book
        END,
        state_id = CASE 
            WHEN state_id IS NULL THEN p_stateid
            ELSE state_id
        END,
        date_updated = CURRENT_TIMESTAMP
    WHERE 
        book_code = p_bookcode 
        AND library_id = p_libraryid;
$$;


--
-- Name: book_book_info_update(text, text, text, integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.book_book_info_update(p_bookcode text, p_bookname text, p_bookplace text, p_isbook integer, p_libraryid uuid) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE lib_books
    SET 
        book_name = p_bookname,
        book_place = p_bookplace,
        date_updated = CURRENT_TIMESTAMP,
        is_book = p_isbook
    WHERE 
        book_code = p_bookcode 
        AND library_id = p_libraryid;
$$;


--
-- Name: book_book_keeping_update(uuid, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.book_book_keeping_update(p_libraryid uuid, p_bookcode text, p_stateid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF (p_stateid <> 5) THEN
        UPDATE bk_books_cache 
        SET is_checked = true, 
            date_checked = CURRENT_TIMESTAMP 
        WHERE book_code = p_bookcode;
    END IF;
    
    UPDATE bk_books 
    SET state_id = p_stateid 
    WHERE book_id = (SELECT id FROM lib_books WHERE book_code = p_bookcode LIMIT 1);
    
    UPDATE lib_books 
    SET state_id = p_stateid 
    WHERE book_code = p_bookcode;
END;
$$;


--
-- Name: book_books_cache_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.book_books_cache_get(p_libraryid uuid) RETURNS TABLE("BookCode" text)
    LANGUAGE sql STABLE
    AS $$
    SELECT book_code AS "BookCode"
    FROM bk_books_cache 
    WHERE library_id = p_libraryid 
      AND is_checked = false;
$$;


--
-- Name: book_service_online_set(uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.book_service_online_set(p_libraryid uuid, p_version text DEFAULT ''::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    -- Защита от NULL
    IF p_libraryid IS NULL THEN
        RAISE EXCEPTION 'p_libraryid cannot be NULL';
    END IF;

    INSERT INTO mon_services (type_id, date_last_online, version, library_id)
    VALUES (2, CURRENT_TIMESTAMP, p_version, p_libraryid)
    ON CONFLICT (library_id, type_id) 
    DO UPDATE SET
        date_last_online = CURRENT_TIMESTAMP,
        version = p_version;
END;
$$;


--
-- Name: core_acs_event_add(text, integer, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.core_acs_event_add(p_readerid text, p_readerport integer, p_rfidcode text, p_clientname text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_last TIMESTAMP;
    v_readerid_int INTEGER;
BEGIN
    -- Преобразуем p_readerid из text в integer
    v_readerid_int := p_readerid::INTEGER;

    SELECT event_date INTO v_last
    FROM acs_events_history 
    WHERE reader_id = v_readerid_int   -- Теперь сравниваем integer = integer
      AND rfid_code = p_rfidcode 
    ORDER BY id DESC
    LIMIT 1;
 
    IF (v_last IS NULL OR EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - v_last)) > 60) THEN
        INSERT INTO acs_events_history (
            event_date, rfid_code, client_name, reader_id, reader_port_number
        )
        VALUES (
            CURRENT_TIMESTAMP,
            p_rfidcode,
            p_clientname,
            v_readerid_int,           -- integer
            p_readerport + 1          -- integer
        );
    END IF;
EXCEPTION
    WHEN invalid_text_representation THEN
        -- Опционально: логирование или игнорирование некорректных readerid
        RAISE WARNING 'Invalid reader ID: "%"', p_readerid;
        -- Можно также просто RETURN; чтобы ничего не вставлять
        RETURN;
END;
$$;


--
-- Name: core_gate_is_alive_set(uuid, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.core_gate_is_alive_set(p_gateid uuid, p_isalive boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_isalive = true THEN
        UPDATE mon_gates 
        SET last_update = CURRENT_TIMESTAMP
        WHERE id = p_gateid;
    END IF;
END;
$$;


--
-- Name: core_is_book_checkout_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.core_is_book_checkout_get(p_bookcode text) RETURNS TABLE("IsCheckout" boolean)
    LANGUAGE sql STABLE
    AS $$
    SELECT EXISTS(
        SELECT 1 
        FROM lib_books 
        WHERE book_code = p_bookcode 
          AND state_id = 2
    ) AS "IsCheckout";
$$;


--
-- Name: core_ss_set_books_ofline(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.core_ss_set_books_ofline(p_sn text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_stationid UUID;
BEGIN
    SELECT id INTO v_stationid
    FROM mon_stations 
    WHERE sn = p_sn
    LIMIT 1;
    
    UPDATE mon_book_shelves_stat 
    SET is_online = false
    WHERE station_id = v_stationid 
      AND EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - date_created)) / 60 > 5;
END;
$$;


--
-- Name: easybookdll_books_cache_add_update(uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.easybookdll_books_cache_add_update(p_deviceid uuid, p_rfid_code text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_libraryid UUID;
    v_book_codes TEXT[];
BEGIN
    SELECT library_id INTO v_libraryid
    FROM mon_devices 
    WHERE id = p_deviceid
    LIMIT 1;
    
    IF v_libraryid IS NOT NULL THEN
        v_book_codes := string_to_array(p_rfid_code, ';');
        
        IF NOT EXISTS (SELECT 1 
                       FROM bk_books_cache 
                       WHERE book_code = ANY(v_book_codes) 
                         AND library_id = v_libraryid)
        AND EXISTS (SELECT 1 
                    FROM bk_books bb 
                    INNER JOIN lib_books b ON b.id = bb.book_id 
                    WHERE b.book_code = ANY(v_book_codes)
                      AND b.library_id = v_libraryid)
        THEN
            INSERT INTO bk_books_cache (book_code, library_id, date_last_update)
            SELECT unnest(v_book_codes), v_libraryid, CURRENT_TIMESTAMP;
        ELSE
            UPDATE bk_books_cache
            SET date_last_update = CURRENT_TIMESTAMP,
                is_checked = false
            WHERE book_code = ANY(v_book_codes) 
              AND library_id = v_libraryid;
        END IF;
    END IF;
END;
$$;


--
-- Name: easybookdll_device_data_add(integer, integer, integer, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.easybookdll_device_data_add(p_softonlinecount integer, p_deviceokcount integer, p_deviceerrorcount integer, p_deviceid text, p_ip text DEFAULT ''::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_lhour INT;
    v_curdate TIMESTAMP := CURRENT_TIMESTAMP;
    v_deviceid_uuid UUID;
BEGIN
    UPDATE mon_devices
    SET last_update = v_curdate
    WHERE id::VARCHAR = p_deviceid;

    v_lhour := EXTRACT(HOUR FROM v_curdate);
    v_deviceid_uuid := p_deviceid::UUID;

    IF EXISTS(SELECT 1 FROM mon_device_data WHERE device_id::VARCHAR = p_deviceid AND hour_value = v_lhour AND date_value = v_curdate::DATE) THEN
        UPDATE mon_device_data
        SET soft_online_count = LEAST(soft_online_count + COALESCE(p_softonlinecount, 0), 60),
            device_ok_count = LEAST(device_ok_count + COALESCE(p_deviceokcount, 0), 3600),
            device_error_count = LEAST(device_error_count + COALESCE(p_deviceerrorcount, 0), 3600)
        WHERE device_id::VARCHAR = p_deviceid 
          AND hour_value = v_lhour 
          AND date_value = v_curdate::DATE;
    ELSE
        INSERT INTO mon_device_data(date_value, hour_value, device_id, soft_online_count, device_ok_count, device_error_count, ip_address)
        VALUES(v_curdate::DATE, v_lhour, v_deviceid_uuid, COALESCE(p_softonlinecount, 0), COALESCE(p_deviceokcount, 0), COALESCE(p_deviceerrorcount, 0), p_ip);
    END IF;
END;
$$;


--
-- Name: easybookdll_device_is_license_valid(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.easybookdll_device_is_license_valid(p_deviceid text) RETURNS TABLE("IsActiveForLicense" integer, "ActiveCount" integer, "OwnerTagServiceCount" integer, "CFG" text, "CFG2" text, "OwnerTagServiceCountHash" text, "OwnerID" uuid, "TS1" text)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        1 AS "IsActiveForLicense", 
        (SELECT COUNT(1)::INTEGER FROM mon_devices) AS "ActiveCount", 
        (SELECT tag_service_count FROM owners LIMIT 1) AS "OwnerTagServiceCount", 
        COALESCE(cfg, '') AS "CFG", 
        COALESCE(cfg2, '') AS "CFG2",
        (SELECT tag_service_count_hash FROM owners LIMIT 1) AS "OwnerTagServiceCountHash",
        (SELECT id FROM owners LIMIT 1) AS "OwnerID",
        (SELECT ts1 FROM owners LIMIT 1) AS "TS1"
    FROM mon_devices m 
    INNER JOIN lib_libraries L ON L.id = m.library_id
    WHERE m.id::VARCHAR = p_deviceid;
$$;


--
-- Name: easybookdll_library_info_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.easybookdll_library_info_get(p_deviceid text) RETURNS TABLE("LibraryID" uuid, "TS" integer, "TS1" text)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        d.library_id AS "LibraryID",
        l.ts::INTEGER AS "TS",          
        o.ts1 AS "TS1"
    FROM mon_devices d
    INNER JOIN lib_libraries l ON l.id = d.library_id
    INNER JOIN owners o ON o.id = l.owner_id
    WHERE d.id = p_deviceid::UUID;
$$;


--
-- Name: easybookdll_reader_info_get(integer, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.easybookdll_reader_info_get(p_id integer, p_rfidcode text, p_deviceid uuid) RETURNS TABLE("ID" integer, "DateCreated" timestamp with time zone, "Name" text, "AbisCode" text, "RFIDCode" text, "SerialNumber" text, "Email" text, "Photo" bytea)
    LANGUAGE plpgsql STABLE
    AS $$
BEGIN
    IF p_rfidcode IS NULL THEN
        p_rfidcode := '-1';
    END IF;
    
    RETURN QUERY
    SELECT 
        r.id,
        r.date_created,
        r.name,
        r.abis_code,
        r.rfid_code,
        r.serial_number,
        r.email,
        (SELECT photo FROM rdr_reader_photos WHERE is_deleted = false AND reader_id = r.id LIMIT 1)
    FROM rdr_readers r
    INNER JOIN mon_devices ms ON ms.id = p_deviceid
    INNER JOIN lib_libraries ll ON ll.id = ms.library_id
    INNER JOIN owners o ON o.id = ll.owner_id
    WHERE r.id = p_id 
       OR r.abis_code = p_rfidcode 
       OR r.rfid_code = p_rfidcode 
       OR r.serial_number = p_rfidcode;
END;
$$;


--
-- Name: easybookdll_reader_modify(integer, integer, text, text, text, text, text, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.easybookdll_reader_modify(p_opid integer, p_readerid integer, p_name text, p_abiscode text, p_rfidcode text, p_serialnumber text, p_email text, p_deviceid uuid, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_ownerid uuid;
    v_row_count integer;
BEGIN
    -- Получаем owner_id
    SELECT o.id INTO v_ownerid
    FROM lib_libraries ll
    INNER JOIN owners o ON o.id = ll.owner_id
    WHERE ll.id = (SELECT library_id FROM mon_devices WHERE id = p_deviceid LIMIT 1)
    LIMIT 1;

    IF v_ownerid IS NULL THEN
        -- Если устройство не найдено или нет owner_id — ничего не делаем
        RETURN;
    END IF;

    IF p_opid = 1 THEN
        -- Проверяем существование читателя по кодам и владельцу
        IF NOT EXISTS (
            SELECT 1 FROM rdr_readers 
            WHERE (abis_code = p_abiscode 
                OR rfid_code = p_rfidcode 
                OR abis_code = p_rfidcode
                OR rfid_code = p_abiscode)
              AND owner_id = v_ownerid
        ) THEN
            -- Вставляем нового
            INSERT INTO rdr_readers (name, abis_code, rfid_code, serial_number, email, owner_id)
            VALUES (p_name, p_abiscode, p_rfidcode, p_serialnumber, p_email, v_ownerid)
            RETURNING id INTO new_id;
        ELSE
            -- Обновляем существующего и возвращаем его ID
            UPDATE rdr_readers
            SET abis_code = p_abiscode,
                rfid_code = p_rfidcode,
                serial_number = p_serialnumber,
                email = COALESCE(p_email, email)
            WHERE (abis_code = p_abiscode 
                OR rfid_code = p_rfidcode 
                OR abis_code = p_rfidcode
                OR rfid_code = p_abiscode)
              AND owner_id = v_ownerid
            RETURNING id INTO new_id;

            GET DIAGNOSTICS v_row_count = ROW_COUNT;
            IF v_row_count = 0 THEN
                new_id := -1;
            END IF;
        END IF;

    ELSIF p_opid = 2 THEN
        -- Обновление по ID
        UPDATE rdr_readers
        SET rfid_code = p_rfidcode,
            abis_code = COALESCE(p_abiscode, abis_code),
            serial_number = COALESCE(p_serialnumber, serial_number),
            email = COALESCE(p_email, email)
        WHERE id = p_readerid
          AND owner_id = v_ownerid
        RETURNING id INTO new_id;

        GET DIAGNOSTICS v_row_count = ROW_COUNT;
        IF v_row_count = 0 THEN
            new_id := -1;
        END IF;
    END IF;
END;
$$;


--
-- Name: fd_add_camera_to_station(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_add_camera_to_station(p_sn text, p_cameraid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_cameralib UUID;
BEGIN
    SELECT d.library_id INTO v_cameralib
    FROM fd_device_cameras dc
    INNER JOIN fd_devices d ON d.id = dc.device_id 
    WHERE dc.id = p_cameraid
    LIMIT 1;
    
    IF v_cameralib IS NOT NULL THEN
        UPDATE mon_stations 
        SET device_camera_id = p_cameraid 
        WHERE sn = p_sn 
          AND library_id = v_cameralib;
    END IF;
END;
$$;


--
-- Name: fd_device_camera_modify(integer, text, text, integer, integer, integer, integer, integer, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_device_camera_modify(p_id integer, p_name text, p_ipaddress text, p_brightness integer, p_contrast integer, p_sharpness integer, p_deviceid integer, p_channelno integer, p_isactive boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_id IS NULL OR p_id <= 0 THEN
        INSERT INTO fd_device_cameras(name, ip, brightness, contrast, sharpness, device_id, channel_no, is_active)
        VALUES (p_name, p_ipaddress, p_brightness, p_contrast, p_sharpness, p_deviceid, p_channelno, p_isactive);
    ELSE 
        UPDATE fd_device_cameras SET 
            name = p_name,
            ip = p_ipaddress,
            brightness = p_brightness,
            contrast = p_contrast,
            sharpness = p_sharpness,
            channel_no = p_channelno,
            is_active = p_isactive,
            device_id = COALESCE(p_deviceid, device_id)
        WHERE id = p_id;
    END IF;
END;
$$;


--
-- Name: fd_device_cameras_by_id_get(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_device_cameras_by_id_get(p_id integer) RETURNS TABLE("ID" integer, "Name" text, "DeviceID" integer, "ChannelNo" integer, "DateCreated" timestamp with time zone, "IsActive" boolean, "DateLastOnline" timestamp with time zone, "HardwareID" text, "IP" text, "Login" text, "Password" text, "Port" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        c.id AS "ID",
        c.name AS "Name",
        c.device_id AS "DeviceID",
        c.channel_no AS "ChannelNo",
        c.date_created AS "DateCreated",
        c.is_active AS "IsActive",
        d.date_last_online AS "DateLastOnline",
        d.hardware_id AS "HardwareID",
        d.ip AS "IP",
        d.login AS "Login",
        d.password AS "Password",
        d.port AS "Port"
    FROM fd_device_cameras c
    INNER JOIN fd_devices d ON d.id = c.device_id
    WHERE c.id = p_id;
$$;


--
-- Name: fd_device_cameras_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_device_cameras_get(p_libraryid uuid) RETURNS TABLE("ID" integer, "Name" text, "DeviceID" integer, "ChannelNo" integer, "DateCreated" timestamp with time zone, "IsActive" boolean, "IP" text, "Brightness" integer, "Contrast" integer, "Sharpness" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        id AS "ID",
        name AS "Name",
        device_id AS "DeviceID",
        channel_no AS "ChannelNo",
        date_created AS "DateCreated",
        is_active AS "IsActive",
        ip AS "IP",
        brightness AS "Brightness",
        contrast AS "Contrast",
        sharpness AS "Sharpness"
    FROM fd_device_cameras
    WHERE device_id IN (SELECT ID FROM fd_devices WHERE library_id = p_libraryid);
$$;


--
-- Name: fd_device_cameras_without_stations_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_device_cameras_without_stations_get(p_libraryid uuid) RETURNS TABLE("ID" integer, "Name" text, "DeviceID" integer, "ChannelNo" integer, "DateCreated" timestamp with time zone, "IsActive" boolean, "IP" text, "Brightness" integer, "Contrast" integer, "Sharpness" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        id,
        name,
        device_id,
        channel_no,
        date_created,
        is_active,
        ip,
        brightness,
        contrast,
        sharpness
    FROM fd_device_cameras
    WHERE device_id IN (SELECT id FROM fd_devices WHERE library_id = p_libraryid) 
      AND id NOT IN (SELECT device_camera_id FROM mon_stations WHERE device_camera_id IS NOT NULL);
$$;


--
-- Name: fd_device_modify(integer, text, integer, text, text, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_device_modify(p_id integer, p_ip text, p_port integer, p_login text, p_password text, p_libraryid uuid, INOUT new_id integer DEFAULT NULL::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_id IS NULL OR p_id <= 0 THEN
        INSERT INTO fd_devices (ip, port, login, password, hardware_id, library_id)
        VALUES (p_ip, p_port, p_login, p_password, '', p_libraryid)
        RETURNING id INTO new_id;
    ELSE
        UPDATE fd_devices 
        SET ip = p_ip, 
            port = p_port, 
            login = p_login, 
            password = p_password
        WHERE id = p_id;

        new_id := p_id;
    END IF;
END;
$$;


--
-- Name: fd_device_online_set(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_device_online_set(p_deviceid integer) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE fd_devices 
    SET date_last_online = CURRENT_TIMESTAMP 
    WHERE id = p_deviceid;
$$;


--
-- Name: fd_device_processed_id_set(integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_device_processed_id_set(p_deviceid integer, p_processedid integer) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE fd_devices 
    SET last_processed_id = p_processedid 
    WHERE id = p_deviceid;
$$;


--
-- Name: fd_devices_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_devices_get(p_libraryid uuid) RETURNS TABLE("ID" integer, "HardwareID" text, "IP" text, "Port" integer, "Login" text, "Password" text, "DateCreated" timestamp with time zone, "DateLastOnline" timestamp with time zone, "LastProcessedId" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT
        id,
        hardware_id,
        ip,
        port,
        login,
        password,
        date_created,
        date_last_online,
        last_processed_id
    FROM fd_devices
    WHERE library_id = p_libraryid;
$$;


--
-- Name: fd_log_add(integer, integer, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_log_add(p_deviceid integer, p_cameraid integer, p_logtype integer, p_message text) RETURNS void
    LANGUAGE sql
    AS $$
    INSERT INTO fd_logs (device_id, device_camera_id, log_type, message)
    VALUES (p_deviceid, p_cameraid, p_logtype, p_message);
$$;


--
-- Name: fd_reader_deleted_photo_ids_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_reader_deleted_photo_ids_get(p_libraryid uuid) RETURNS TABLE("ReaderID" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT DISTINCT p.reader_id
    FROM rdr_reader_photos p
    INNER JOIN rdr_readers r ON r.id = p.reader_id
    WHERE p.is_deleted = true
      AND NOT EXISTS (
          SELECT 1 
          FROM rdr_reader_photos p2 
          WHERE p2.reader_id = p.reader_id 
            AND p2.is_deleted = false
      )
      AND r.owner_id = (SELECT owner_id FROM lib_libraries WHERE id = p_libraryid LIMIT 1);
$$;


--
-- Name: fd_reader_photo_add(integer, bytea); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_reader_photo_add(p_readerid integer, p_photo bytea) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    UPDATE rdr_reader_photos 
    SET photo = NULL, 
        is_deleted = true
    WHERE reader_id = p_readerid;
    
    INSERT INTO rdr_reader_photos(reader_id, photo)
    VALUES(p_readerid, p_photo);
END;
$$;


--
-- Name: fd_reader_photo_by_id_get(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_reader_photo_by_id_get(p_readerid integer) RETURNS TABLE("Photo" bytea)
    LANGUAGE sql STABLE
    AS $$
    SELECT p.photo
    FROM rdr_reader_photos p
    INNER JOIN rdr_readers r ON r.id = p.reader_id
    WHERE p.reader_id = p_readerid 
      AND p.is_deleted = false;
$$;


--
-- Name: fd_reader_photo_delete(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_reader_photo_delete(p_readerid integer) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE rdr_reader_photos 
    SET photo = NULL, 
        is_deleted = true
    WHERE reader_id = p_readerid;
$$;


--
-- Name: fd_reader_photos_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_reader_photos_get(p_startid integer, p_libraryid uuid) RETURNS TABLE("ID" integer, "ReaderID" integer, "Name" text, "Photo" bytea)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        p.id,
        p.reader_id,
        r.name,
        p.photo
    FROM rdr_reader_photos p
    INNER JOIN rdr_readers r ON r.ID = p.reader_id
    WHERE p.ID > p_startid 
      AND p.is_deleted = false 
      AND r.owner_id = (SELECT owner_id FROM lib_libraries WHERE id = p_libraryid LIMIT 1);
$$;


--
-- Name: fd_reader_with_photos_ids_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_reader_with_photos_ids_get(p_startid integer, p_libraryid uuid) RETURNS TABLE("ID" integer, "ReaderID" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT p.id, p.reader_id
    FROM rdr_reader_photos p
    INNER JOIN rdr_readers r ON r.id = p.reader_id
    WHERE p.id > p_startid 
      AND p.is_deleted = false 
      AND r.owner_id = (SELECT owner_id FROM lib_libraries WHERE id = p_libraryid LIMIT 1);
$$;


--
-- Name: fd_safe_keeper_camera_full_info_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_safe_keeper_camera_full_info_get(p_sn text) RETURNS TABLE("CameraID" integer, "HardwareID" text, "IP" text, "Port" integer, "Login" text, "Password" text, "ChannelNo" integer, "CameraIP" text, "Brightness" integer, "Contrast" integer, "Sharpness" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        s.device_camera_id,
        d.hardware_id,
        d.ip,
        d.port,
        d.login,
        d.password,
        dc.channel_no,
        dc.ip,
        dc.brightness,
        dc.contrast,
        dc.sharpness
    FROM mon_stations s
    INNER JOIN fd_device_cameras dc ON dc.id = s.device_camera_id
    INNER JOIN fd_devices d ON d.id = dc.device_id
    WHERE s.sn = p_sn 
      AND dc.is_active = true
    LIMIT 1;
$$;


--
-- Name: fd_safe_keeper_camera_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_safe_keeper_camera_get(p_sn text) RETURNS TABLE("CameraID" integer, "HardwareID" text, "IP" text, "Port" integer, "Login" text, "Password" text, "ChannelNo" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        s.device_camera_id,
        d.hardware_id,
        d.ip,
        d.port,
        d.login,
        d.password,
        dc.channel_no
    FROM mon_stations s
    INNER JOIN fd_device_cameras dc ON dc.id = s.device_camera_id
    INNER JOIN fd_devices d ON d.id = dc.device_id
    WHERE s.sn = p_sn 
      AND dc.is_active = true
    LIMIT 1;
$$;


--
-- Name: fd_service_online_set(uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_service_online_set(p_libraryid uuid, p_version text DEFAULT ''::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    INSERT INTO mon_services (type_id, date_last_online, version, library_id)
    VALUES (1, CURRENT_TIMESTAMP, p_version, p_libraryid)
    ON CONFLICT (library_id, type_id) 
    DO UPDATE SET
        date_last_online = CURRENT_TIMESTAMP,
        version = EXCLUDED.version;
END;
$$;


--
-- Name: fd_station_camera_add_modify(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_station_camera_add_modify(p_sn text, p_cameraid integer) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE mon_stations
    SET device_camera_id = p_cameraid
    WHERE sn = p_sn;
$$;


--
-- Name: fd_station_camera_full_info_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_station_camera_full_info_get(p_sn text) RETURNS TABLE("CameraID" integer, "HardwareID" text, "IP" text, "Port" integer, "Login" text, "Password" text, "ChannelNo" integer, "CameraIP" text, "Brightness" integer, "Contrast" integer, "Sharpness" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        s.device_camera_id,
        d.hardware_id,
        d.ip,
        d.port,
        d.login,
        d.password,
        dc.channel_no,
        dc.ip,
        dc.brightness,
        dc.contrast,
        dc.sharpness
    FROM mon_stations s
    INNER JOIN fd_device_cameras dc ON dc.id = s.device_camera_id
    INNER JOIN fd_devices d ON d.id = dc.device_id
    WHERE s.sn = p_sn 
      AND dc.is_active = true
    LIMIT 1;
$$;


--
-- Name: fd_station_camera_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_station_camera_get(p_sn text) RETURNS TABLE("CameraID" integer, "HardwareID" text, "IP" text, "Port" integer, "Login" text, "Password" text, "ChannelNo" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT 
		s.device_camera_id AS "CameraID",
		d.hardware_id AS "HardwareID",
		d.ip::text AS "IP",
		d.port AS "Port",
		d.login AS "Login",
		d.password AS "Password",
		dc.channel_no AS "ChannelNo"
	FROM mon_stations s
	INNER JOIN fd_device_cameras dc ON dc.id = s.device_camera_id
	INNER JOIN fd_devices d ON d.id = dc.device_id
	WHERE s.sn = p_sn 
	  AND dc.is_active = true
	LIMIT 1;
END;
$$;


--
-- Name: fd_stations_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_stations_get(p_libraryid uuid) RETURNS TABLE("ID" uuid, "Name" text, "StationTypeID" integer, "StationTypeName" text, "SN" text, "DeviceCameraID" integer)
    LANGUAGE sql STABLE
    AS $$
    SELECT
        s.id,
        s.name,
        s.station_type_id,
        st.name,
        s.sn,
        s.device_camera_id
    FROM mon_stations s
    LEFT JOIN fd_device_cameras dc ON dc.id = s.device_camera_id
    INNER JOIN mon_station_types st ON st.id = s.station_type_id
    WHERE s.library_id = p_libraryid;
$$;


--
-- Name: fd_visitor_photo_add(integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, bytea, bytea); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fd_visitor_photo_add(p_cameraid integer, p_readerid integer, p_age integer, p_attraction integer, p_beard integer, p_emotion integer, p_eyes integer, p_glasses integer, p_mask integer, p_mouth integer, p_similarity integer, p_race integer, p_sex integer, p_photo bytea, p_fullcadre bytea) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_id INT;
BEGIN
    BEGIN
        INSERT INTO fd_visitors_photo(CameraID, ReaderID, Photo)
        VALUES(p_cameraid, p_readerid, p_photo)
        RETURNING ID INTO v_id;
        
        IF v_id IS NOT NULL THEN
            INSERT INTO fd_visitors_full_cadre(VisitorPhotoID, FullCadre)
            VALUES(v_id, p_fullcadre);
            
            INSERT INTO fd_visitors_values(VisitorPhotoID, Age, Attraction, Beard, Emotion, Eyes, Glasses, Mask, Mouth, Similarity, Race, Sex)
            VALUES(v_id, p_age, p_attraction, p_beard, p_emotion, p_eyes, p_glasses, p_mask, p_mouth, p_similarity, p_race, p_sex);
        END IF;
        
    EXCEPTION
        WHEN OTHERS THEN
            RAISE;
    END;
END;
$$;


--
-- Name: fn_fio_get(text, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.fn_fio_get(p_firstname text, p_lastname text, p_patronimic text) RETURNS text
    LANGUAGE sql IMMUTABLE
    AS $$
    SELECT COALESCE(p_lastname, '') || ' ' || COALESCE(SUBSTRING(p_firstname FROM 1 FOR 1), '') || '.' || COALESCE(SUBSTRING(p_patronimic FROM 1 FOR 1), '') || '.';
$$;


--
-- Name: gate_gate_counters_add(uuid, timestamp with time zone, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_gate_counters_add(p_gateid uuid, p_date timestamp with time zone, p_valuein integer, p_valueout integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_valuedate TIMESTAMP;
BEGIN
    v_valuedate := DATE_TRUNC('hour', p_date);
    
    INSERT INTO mon_gates_counters (gate_id, value_in, value_out, date_value)
    VALUES (p_gateid, p_valuein, p_valueout, v_valuedate)
    ON CONFLICT (gate_id, date_value) 
    DO UPDATE SET
        value_in = mon_gates_counters.value_in + EXCLUDED.value_in,
        value_out = mon_gates_counters.value_out + EXCLUDED.value_out;
END;
$$;


--
-- Name: gate_gate_eas_add(uuid, timestamp with time zone, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_gate_eas_add(p_gateid uuid, p_date timestamp with time zone, p_bookcode text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_last TIMESTAMP;
    v_libraryid UUID;
BEGIN
    SELECT date_value INTO v_last
    FROM mon_gates_eas 
    WHERE gate_id = p_gateid AND uid = p_bookcode 
    ORDER BY date_value DESC 
    LIMIT 1;
    
    SELECT library_id INTO v_libraryid
    FROM mon_gates 
    WHERE id = p_gateid
    LIMIT 1;
    
    IF v_libraryid IS NULL THEN
        RETURN;
    END IF;
    
    IF (v_last IS NULL OR EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - v_last)) > 60) THEN
        INSERT INTO mon_gates_eas(gate_id, date_value, uid)
        VALUES (p_gateid, CURRENT_TIMESTAMP, p_bookcode);
        
        IF NOT EXISTS (SELECT 1 FROM lib_books WHERE book_code = p_bookcode AND library_id = v_libraryid) THEN
            INSERT INTO lib_books(book_code, library_id) 
            VALUES(p_bookcode, v_libraryid);
        END IF;
    END IF;
END;
$$;


--
-- Name: gate_gate_event_add(uuid, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_gate_event_add(p_gateid uuid, p_eventid integer, p_message text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_recid integer;
    v_issingleevent boolean;
BEGIN
    SELECT is_single_event INTO v_issingleevent
    FROM mon_events WHERE id = p_eventid;
    
    IF NOT v_issingleevent THEN
        SELECT id INTO v_recid
        FROM mon_gates_event_history
        WHERE event_id = p_eventid 
          AND gate_id = p_gateid 
          AND DATE_TRUNC('hour', date_value) = DATE_TRUNC('hour', CURRENT_TIMESTAMP);
        
        IF v_recid IS NOT NULL THEN
            UPDATE mon_gates_event_history 
            SET count = count + 1 
            WHERE id = v_recid;
        ELSE
            INSERT INTO mon_gates_event_history (gate_id, event_id, date_value, comment)
            VALUES(p_gateid, p_eventid, CURRENT_TIMESTAMP, p_message);
        END IF;
    ELSE
        INSERT INTO mon_gates_event_history (gate_id, event_id, date_value, comment)
        VALUES(p_gateid, p_eventid, CURRENT_TIMESTAMP, p_message);
    END IF;
END;
$$;


--
-- Name: gate_gate_online_set(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_gate_online_set(p_gateid uuid) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE mon_gates 
    SET last_update = CURRENT_TIMESTAMP
    WHERE id = p_gateid;
$$;


--
-- Name: gate_gates_info_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_gates_info_get(p_libraryid uuid) RETURNS TABLE("ID" uuid, "Name" text, "IP" text, "Port" integer, "TypeID" integer, "LastUpdate" timestamp with time zone)
    LANGUAGE sql STABLE
    AS $$
    SELECT 
        id AS "ID",
        name AS "Name",
        ip AS "IP",
        port AS "Port",
        cfg_type_id AS "TypeID",
        last_update AS "LastUpdate"
    FROM mon_gates
    WHERE library_id = p_libraryid;
$$;


--
-- Name: gate_monogate_counter_modify(integer, uuid, text, text, integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_monogate_counter_modify(p_opid integer, p_id uuid, p_name text, p_ip text, p_port integer, p_gateid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_opid = 1 THEN
        INSERT INTO mon_mono_gate_count_devices (id, name, ip, port, gate_id)
        VALUES (gen_random_uuid(), p_name, p_ip, p_port, p_gateid);
    END IF;

    IF p_opid = 2 THEN
        UPDATE mon_mono_gate_count_devices
        SET name = p_name,
            ip = p_ip,
            port = p_port,
            gate_id = p_gateid
        WHERE id = p_id;
    END IF;

    IF p_opid = 3 THEN
        DELETE FROM mon_mono_gate_count_devices
        WHERE id = p_id;
    END IF;
END;
$$;


--
-- Name: gate_monogate_counter_online_set(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_monogate_counter_online_set(p_id uuid) RETURNS void
    LANGUAGE sql
    AS $$
    UPDATE mon_mono_gate_count_devices
    SET date_last_online = CURRENT_TIMESTAMP
    WHERE id = p_id;
$$;


--
-- Name: gate_monogate_counters_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_monogate_counters_get(p_gateid uuid) RETURNS TABLE("ID" uuid, "Name" text, "IP" text, "GateID" uuid, "DateLastOnline" timestamp with time zone)
    LANGUAGE sql STABLE
    AS $$
    SELECT id AS "ID",
           name AS "Name",
           ip AS "IP",
           gate_id AS "GateID",
           date_last_online AS "DateLastOnline"
    FROM mon_mono_gate_count_devices
    WHERE gate_id = p_gateid;
$$;


--
-- Name: gate_service_online_set(integer, uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.gate_service_online_set(p_typeid integer, p_libraryid uuid, p_version text DEFAULT ''::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    INSERT INTO mon_services (type_id, date_last_online, version, library_id)
    VALUES (p_typeid, CURRENT_TIMESTAMP, p_version, p_libraryid)
    ON CONFLICT (library_id, type_id) 
    DO UPDATE SET
        date_last_online = CURRENT_TIMESTAMP,
        version = EXCLUDED.version;
END;
$$;


--
-- Name: it_book_cases_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_book_cases_get(p_sn text) RETURNS TABLE("BookCaseID" integer, "BookCaseName" text, "BookShelvesCount" integer, "LibraryID" uuid, "Coordinates" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        bc.id AS "BookCaseID",
        bc.name AS "BookCaseName",
        COUNT(bs.id)::integer AS "BookShelvesCount",
        bc.library_id AS "LibraryID",
        bc.coordinates AS "Coordinates"
    FROM bk_book_cases bc
    INNER JOIN lib_libraries l ON l.id = bc.library_id
    LEFT JOIN bk_book_shelves bs ON bs.book_case_id = bc.id
    WHERE bc.library_id = (
        SELECT library_id FROM mon_stations WHERE sn = p_sn LIMIT 1
    )
    GROUP BY bc.id, bc.name, bc.library_id, bc.coordinates;
END;
$$;


--
-- Name: it_book_full_text_search(text, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_book_full_text_search(p_searchstring text, p_sn text, p_rank integer DEFAULT 0) RETURNS TABLE("BookID" integer, "BookName" text, "AuthorsName" text, "BookCode" text, "BookCellID" uuid, "BookCellNumber" integer, "BookShelfID" integer, "BookShelfName" text, "BookCaseID" integer, "BookCaseName" text, "BookCaseSectionID" integer, "BookCaseSectionName" text, "SmartShelfName" text, "RankValue" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_library_id uuid;
    v_tsquery tsquery;
BEGIN
    -- Получаем библиотеку по уникальному номеру станции
    SELECT library_id INTO v_library_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_library_id IS NULL THEN
        RETURN;
    END IF;

    -- Валидация и нормализация порога ранга (0–1000)
    IF p_rank < 0 THEN
        p_rank := 0;
    ELSIF p_rank > 1000 THEN
        p_rank := 1000;
    END IF;

    -- Преобразуем поисковую строку в tsquery (регистронезависимо, простой текст)
    v_tsquery := plainto_tsquery('russian', p_searchstring);

    IF v_tsquery IS NULL OR TRIM(p_searchstring) = '' THEN
        RETURN;
    END IF;

    RETURN QUERY
    SELECT
        b.book_id::integer AS "BookID",
        lb.book_name::text AS "BookName",
        lb.authors_name::text AS "AuthorsName",
        lb.book_code::text AS "BookCode",
        b.cell_id::uuid AS "BookCellID",
        bcl.number::integer AS "BookCellNumber", 
        bs.id::integer AS "BookShelfID",
        bs.name::text AS "BookShelfName",
        bc.id::integer AS "BookCaseID",
        bc.name::text AS "BookCaseName",
        bcs.id::integer AS "BookCaseSectionID",
        bcs.name::text AS "BookCaseSectionName",
        CASE WHEN bss.is_online = true THEN s.name::text ELSE NULL END AS "SmartShelfName",
        GREATEST(0, LEAST(1000, (
            ts_rank_cd(
                to_tsvector('russian', COALESCE(lb.book_name, '') || ' ' || COALESCE(lb.authors_name, '')),
                v_tsquery,
                1  -- нормализация по длине документа
            ) * 1000
        )::integer)) AS "RankValue"
    FROM bk_books b
    INNER JOIN lib_books lb ON lb.id = b.book_id
    INNER JOIN bk_book_cells bcl ON bcl.id = b.cell_id
    INNER JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
    INNER JOIN bk_book_cases bc ON bc.id = bs.book_case_id
    INNER JOIN bk_book_case_sections bcs ON bcs.id = bc.book_case_section_id
    LEFT JOIN mon_book_shelves_stat bss ON bss.book_id = b.book_id
    LEFT JOIN mon_stations s ON s.id = bss.station_id
    WHERE bc.library_id = v_library_id
      AND (b.state_id = 1 OR (b.state_id = 5 AND bss.is_online = true))
      AND to_tsvector('russian', COALESCE(lb.book_name, '') || ' ' || COALESCE(lb.authors_name, '')) @@ v_tsquery
      AND (
          ts_rank_cd(
              to_tsvector('russian', COALESCE(lb.book_name, '') || ' ' || COALESCE(lb.authors_name, '')),
              v_tsquery,
              1
          ) * 1000
      ) >= p_rank
    ORDER BY "RankValue" DESC
    LIMIT 100;

END;
$$;


--
-- Name: it_book_keeping_search(text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_book_keeping_search(p_searchstring text, p_sn text) RETURNS TABLE("BookID" integer, "BookName" text, "AuthorsName" text, "BookPlace" text, "BookCode" text, "BookCellID" uuid, "BookCellNumber" integer, "BookShelfID" integer, "BookShelfName" text, "BookCaseID" integer, "BookCaseName" text, "BookCaseSectionID" integer, "BookCaseSectionName" text, "SmartShelfName" text)
    LANGUAGE plpgsql
    AS $_$
DECLARE
    words text[];
    where_clause text := 'TRUE';
    v_library_id uuid;
BEGIN
    -- Получаем library_id по уникальному номеру станции
    SELECT library_id INTO v_library_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_library_id IS NULL THEN
        RETURN;
    END IF;

    IF NULLIF(TRIM(p_searchstring), '') IS NULL THEN
        RETURN;
    END IF;

    words := ARRAY(
        SELECT TRIM(w)
        FROM UNNEST(REGEXP_SPLIT_TO_ARRAY(p_searchstring, '\s+')) AS w
        WHERE TRIM(w) <> ''
    );

    FOR i IN 1..array_length(words, 1) LOOP
        where_clause := where_clause || 
            format(' AND (lb.book_name ILIKE %L OR lb.authors_name ILIKE %L)', 
                   '%' || words[i] || '%', 
                   '%' || words[i] || '%');
    END LOOP;

    RETURN QUERY EXECUTE '
        SELECT
            lb.id::integer AS "BookID",
            lb.book_name::text AS "BookName",
            lb.authors_name::text AS "AuthorsName",
            lb.book_place::text AS "BookPlace",
            lb.book_code::text AS "BookCode",
            COALESCE(b.cell_id, ''00000000-0000-0000-0000-000000000000''::uuid) AS "BookCellID",
            COALESCE(bcl.number, 0)::int AS "BookCellNumber",
            COALESCE(bs.id, 0)::integer AS "BookShelfID",
            bs.name::text AS "BookShelfName",
            COALESCE(bc.id, 0)::integer AS "BookCaseID",
            bc.name::text AS "BookCaseName",
            COALESCE(bcs.id, 0)::integer AS "BookCaseSectionID",
            bcs.name::text AS "BookCaseSectionName",
            CASE WHEN bss.is_online = true THEN s.name::text ELSE NULL END AS "SmartShelfName"
        FROM lib_books lb
        LEFT JOIN bk_books b ON lb.id = b.book_id
        LEFT JOIN bk_book_cells bcl ON bcl.id = b.cell_id
        LEFT JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
        LEFT JOIN bk_book_cases bc ON bc.id = bs.book_case_id
        LEFT JOIN bk_book_case_sections bcs ON bcs.id = bc.book_case_section_id
        LEFT JOIN mon_book_shelves_stat bss ON bss.book_id = b.book_id
        LEFT JOIN mon_stations s ON s.id = bss.station_id
        WHERE (' || where_clause || ')
          AND lb.library_id = $1
          AND (b.state_id = 1 OR (b.state_id = 5 AND bss.is_online = true) OR b.book_id IS NULL)
    ' USING v_library_id;

END;
$_$;


--
-- Name: it_book_rate2_get(text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_book_rate2_get(p_bookcode text, p_sn text) RETURNS TABLE("BookRate" integer, "BookRateCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_library_id UUID;
    v_owner_id UUID;
    v_book_id INTEGER;
    v_rate numeric;
    v_count integer;
BEGIN
    -- По умолчанию — нулевые значения
    v_rate := 0;
    v_count := 0;

    IF p_bookcode IS NOT NULL THEN
        -- Получаем library_id
        SELECT library_id INTO v_library_id
        FROM mon_stations
        WHERE sn = p_sn
        LIMIT 1;

        IF v_library_id IS NOT NULL THEN
            -- Получаем owner_id
            SELECT owner_id INTO v_owner_id
            FROM lib_libraries
            WHERE id = v_library_id
            LIMIT 1;

            IF v_owner_id IS NOT NULL THEN
                -- Получаем book_id
                SELECT lb.id INTO v_book_id
                FROM lib_books lb
                INNER JOIN lib_libraries lib ON lb.library_id = lib.id
                WHERE lb.book_code = p_bookcode
                  AND lib.owner_id = v_owner_id
                LIMIT 1;

                IF v_book_id IS NOT NULL THEN
                    -- Получаем реальные рейтинги: округляем AVG до целого
                    SELECT
                        COALESCE(ROUND(AVG(rate)), 0)::integer,
                        COUNT(id)::integer
                    INTO v_rate, v_count
                    FROM lib_book_user_ratings
                    WHERE book_id = v_book_id;
                END IF;
            END IF;
        END IF;
    END IF;

    -- Возвращаем ровно одну строку
    RETURN QUERY SELECT v_rate::integer, v_count;
END;
$$;


--
-- Name: it_book_rate_get(text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_book_rate_get(p_bookcode text, p_sn text) RETURNS TABLE("BookRate" double precision)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        COALESCE(AVG(Rate::DOUBLE PRECISION), 0.01) AS "BookRate"
    FROM lib_book_user_ratings br
    WHERE book_id = (
        SELECT id 
        FROM lib_books 
        WHERE book_code = p_bookcode 
          AND library_id = (SELECT library_id FROM mon_stations WHERE sn = p_sn LIMIT 1)
        LIMIT 1
    );
END;
$$;


--
-- Name: it_book_shelves_event_add(text, text, integer, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_book_shelves_event_add(p_bookcode text, p_bookname text, p_isonshelf integer, p_sn text, p_takescount integer DEFAULT 0) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_bookid int;
    v_stationid uuid;
    v_libraryid uuid;
BEGIN
    -- Получаем StationID и LibraryID по SN
    SELECT id, library_id INTO v_stationid, v_libraryid
    FROM mon_stations
    WHERE sn = p_sn  -- теперь ясно: sn — столбец, p_sn — параметр
    LIMIT 1;

    -- Если станция не найдена — выходим
    IF v_stationid IS NULL THEN
        RETURN;
    END IF;

    -- Пытаемся найти книгу по коду
    SELECT id INTO v_bookid
    FROM lib_books
    WHERE book_code = p_bookcode;  -- предполагается, что столбец называется book_code

    -- Если книга не существует — вставляем новую
    IF v_bookid IS NULL THEN
        INSERT INTO lib_books (date_created, book_code, book_name, date_updated, library_id)
        VALUES (NOW(), p_bookcode, p_bookname, NOW(), v_libraryid)
        RETURNING id INTO v_bookid;
    ELSE
        -- Если имя пустое — обновляем
        UPDATE lib_books
        SET date_updated = NOW(),
            book_name = p_bookname
        WHERE id = v_bookid
          AND (book_name IS NULL OR TRIM(book_name) = '');
    END IF;

    -- Проверяем, существует ли запись в mon_book_shelves_stat
    IF EXISTS (
        SELECT 1 FROM mon_book_shelves_stat
        WHERE station_id = v_stationid AND book_id = v_bookid
    ) THEN
        -- Обновляем существующую запись
        UPDATE mon_book_shelves_stat
        SET is_online = (p_isonshelf = 1),  -- если isonshelf: 1/0 → bool
            count_takes = p_takescount,
            date_created = NOW()
        WHERE station_id = v_stationid AND book_id = v_bookid;
    ELSE
        -- Вставляем новую запись
        INSERT INTO mon_book_shelves_stat (station_id, is_online, book_id, count_takes)
        VALUES (v_stationid, (p_isonshelf = 1), v_bookid, p_takescount);
    END IF;

END;
$$;


--
-- Name: it_book_user_rating_add(text, text, text, text, integer, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_book_user_rating_add(p_bookcode text, p_bookname text, p_usercode text, p_username text, p_rate integer, p_volumecomplete integer, p_sn text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    book_id INTEGER;
    user_id INTEGER;
BEGIN
    SELECT id INTO book_id FROM lib_books WHERE book_code = p_bookcode LIMIT 1;

    IF (book_id IS NULL) THEN
        INSERT INTO lib_books(book_code, book_name, is_book, library_id)
        VALUES (p_bookcode, p_bookname, 1, (SELECT library_id FROM mon_stations WHERE sn = p_sn LIMIT 1));
        SELECT id INTO book_id FROM lib_books WHERE book_code = p_bookcode LIMIT 1;
    END IF;

    SELECT id INTO user_id FROM rdr_readers WHERE abis_code = p_usercode LIMIT 1;

    IF (user_id IS NULL) THEN
        INSERT INTO rdr_readers(abis_code, name, owner_id)
        VALUES (p_usercode, p_username, 
            (SELECT owner_id FROM lib_libraries l
            INNER JOIN mon_stations s ON s.library_id = l.id
            WHERE s.sn = p_sn LIMIT 1));
        SELECT id INTO user_id FROM rdr_readers WHERE abis_code = p_usercode LIMIT 1;
    END IF;

    INSERT INTO lib_book_user_ratings(book_id, rate, reader_id, volume_complete)
    VALUES (book_id, p_rate, user_id, p_volumecomplete);
END;
$$;


--
-- Name: it_books_cache_add_update(text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_books_cache_add_update(p_sn text, p_bookcode text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_library_id UUID;
BEGIN
    -- Получаем library_id по SN станции
    SELECT library_id INTO v_library_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_library_id IS NOT NULL THEN
        -- Проверяем: записи нет в кэше, но книга существует в библиотеке
        IF NOT EXISTS (
            SELECT 1
            FROM bk_books_cache
            WHERE book_code = p_bookcode
              AND library_id = v_library_id  -- ← переменная с префиксом
        )
        AND EXISTS (
            SELECT 1
            FROM bk_books bb
            INNER JOIN lib_books b ON b.id = bb.book_id
            WHERE b.book_code = p_bookcode
              AND b.library_id = v_library_id  -- ← переменная
        )
        THEN
            INSERT INTO bk_books_cache (book_code, library_id, date_last_update)
            VALUES (p_bookcode, v_library_id, CURRENT_TIMESTAMP);
        ELSE
            UPDATE bk_books_cache
            SET date_last_update = CURRENT_TIMESTAMP,
                is_checked = FALSE
            WHERE book_code = p_bookcode
              AND library_id = v_library_id;  -- ← переменная
        END IF;
    END IF;
END;
$$;


--
-- Name: it_coordinates_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_coordinates_get(p_sn text) RETURNS SETOF text
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT COALESCE(coordinates, '0;0')::text
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;
END;
$$;


--
-- Name: it_gpt_get_active_templates_for_station(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_gpt_get_active_templates_for_station(p_sn text) RETURNS TABLE("ID" integer, "QuestionText" text, "AnswerText" text, "FontSize" integer, "FontColor" text, "BorderColor" text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id UUID;
    v_library_id UUID;
BEGIN
    SELECT id, library_id INTO v_station_id, v_library_id
    FROM mon_stations 
    WHERE sn = p_sn 
    LIMIT 1;

    RETURN QUERY
    SELECT 
        t.id AS "ID",
        t.question_text AS "QuestionText",
        t.answer_text AS "AnswerText",
        t.font_size AS "FontSize",
        t.font_color AS "FontColor",
        t.border_color AS "BorderColor"
    FROM gpt_voice_response_templates t
    JOIN gpt_voice_template_stations ts ON t.id = ts.template_id
    WHERE t.library_id = v_library_id
        AND ts.station_id = v_station_id
        AND t.is_active = TRUE
        AND ts.is_active = TRUE
    ORDER BY t.question_text;
END;
$$;


--
-- Name: it_gpt_insert_voice_interaction(text, text, text, numeric, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_gpt_insert_voice_interaction(p_sn text, p_requesttext text, p_responsetext text, p_sessionduration numeric DEFAULT NULL::numeric, p_rfidcode text DEFAULT NULL::text, p_readerid integer DEFAULT NULL::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id UUID;
    v_library_id UUID;
BEGIN
    -- Получаем StationID и LibraryID по SN
    SELECT id, library_id INTO v_station_id, v_library_id
    FROM mon_stations 
    WHERE sn = p_sn
    LIMIT 1;

    IF v_station_id IS NULL THEN
        RETURN 0; -- Станция не найдена
    END IF;

    IF v_library_id IS NULL THEN
        RETURN 0; -- LibraryID обязателен, но отсутствует у станции
    END IF;

    INSERT INTO gpt_voice_interactions 
        (station_id, library_id, request_text, response_text, session_duration, rfid_code, reader_id, date_created)
    VALUES 
        (v_station_id, v_library_id, p_requesttext, p_responsetext, p_sessionduration, p_rfidcode, p_readerid, CURRENT_TIMESTAMP);

    RETURN 1;
END;
$$;


--
-- Name: it_libraries_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_libraries_get(p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "Fullname" text, "Manager" text, "Phone" text, "Address" text, "TagServiceCount" integer, "TagServiceCountHash" text, "TS" integer, "DateCreated" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        l.id::uuid,
        l.name::text,
        l.fullname::text,      
        l.manager::text,
        l.phone::text,
        l.address::text,
        l.tag_service_count::integer,
        l.tag_service_count_hash::text,
        l.ts::integer,
        l.date_created::timestamptz
    FROM lib_libraries l
    WHERE l.owner_id = p_ownerid
    ORDER BY l.name;
END;
$$;


--
-- Name: it_order_book_processed_set(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_order_book_processed_set(p_bookrfid text, p_orderid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
  UPDATE sk_order_body SET is_processed = TRUE, date_processed = CURRENT_TIMESTAMP
  WHERE order_id = p_orderid AND book_rfid = p_bookrfid;
END;
$$;


--
-- Name: it_order_book_verified_set(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_order_book_verified_set(p_bookrfid text, p_orderid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
  UPDATE sk_order_body SET is_verified = TRUE, date_verified = CURRENT_TIMESTAMP
  WHERE order_id = p_orderid AND book_rfid = p_bookrfid;
END;
$$;


--
-- Name: it_order_state_modify(integer, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_order_state_modify(p_orderid integer, p_sn text, p_stateid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    cell_no INTEGER;
    station_type INTEGER;
BEGIN
    IF p_stateid >= 4 THEN
        BEGIN
            cell_no := (SELECT cell_number FROM sk_orders
            WHERE safe_keeper_id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1) AND id = p_orderid
            LIMIT 1);

            IF cell_no IS NOT NULL THEN
                UPDATE sk_orders 
                SET state_id = p_stateid
                WHERE safe_keeper_id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1) AND id = p_orderid;

                UPDATE sk_safekeepers
                SET cells_state = ~((~cells_state)|POWER(2::BIGINT, cell_no - 1))
                WHERE id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1);
            ELSE
                SELECT station_type_id INTO station_type FROM mon_stations WHERE sn = p_sn LIMIT 1;
                IF (station_type = 9) THEN
                    UPDATE sk_orders 
                    SET state_id = p_stateid
                    WHERE safe_keeper_id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1) AND id = p_orderid;
                END IF;
            END IF;
        END;
    ELSE
        UPDATE sk_orders 
        SET state_id = p_stateid
        WHERE safe_keeper_id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1) AND id = p_orderid;
    END IF;
END;
$$;


--
-- Name: it_orders_bodies_by_safe_keeper_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_orders_bodies_by_safe_keeper_get(p_sn text) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "BookRFID" text, "BookInfo" text, "IsVerified" boolean, "DateVerified" timestamp with time zone, "OrderId" integer, "OrderNumber" text, "ReaderFIO" text, "ReaderRFID" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT b.id AS "Id",
		b.date_created AS "DateCreated",
		b.book_rfid AS "BookRFID",
		b.book_info AS "BookInfo",
		b.is_verified AS "IsVerified",
		b.date_verified AS "DateVerified",
		b.order_id AS "OrderId",
		o.number AS "OrderNumber",
		o.reader_fio AS "ReaderFIO",
		o.reader_rfid AS "ReaderRFID"
	FROM sk_order_body b
	INNER JOIN sk_orders o ON o.id = b.order_id
	INNER JOIN mon_stations s ON s.id = o.safe_keeper_id
	WHERE s.sn = p_sn AND o.state_id IN (2,3);
END;
$$;


--
-- Name: it_orders_by_rfid_code_get(text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_orders_by_rfid_code_get(p_sn text, p_rfidcode text) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "Number" text, "CellNumber" integer, "ReaderRFID" text, "StateId" integer, "ReaderFIO" text, "ModifierFIO" text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    reader_id INTEGER;
BEGIN
    reader_id := (SELECT id FROM rdr_readers WHERE abis_code = p_rfidcode OR rfid_code = p_rfidcode OR serial_number = p_rfidcode LIMIT 1);

    RETURN QUERY
    SELECT DISTINCT o.id AS "Id",
        o.date_created AS "DateCreated",
        o.number AS "Number",
        o.cell_number AS "CellNumber",
        o.reader_rfid AS "ReaderRFID",
        o.state_id AS "StateId",
        o.reader_fio AS "ReaderFIO",
        fn_fio_get(l.first_name, l.last_name, l.second_name) AS "ModifierFIO"
    FROM sk_orders o
    INNER JOIN mon_stations s ON s.id = o.safe_keeper_id
    INNER JOIN auth_logins l ON l.id = o.login_id
    LEFT JOIN rdr_readers r ON r.abis_code = o.reader_rfid OR r.rfid_code = o.reader_rfid OR r.serial_number = o.reader_rfid
    WHERE s.sn = p_sn
    AND 
    ((o.reader_rfid = p_rfidcode) OR (r.id = reader_id))
    AND
    o.state_id IN (1, 2, 3);
END;
$$;


--
-- Name: it_orders_by_safe_keeper_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_orders_by_safe_keeper_get(p_sn text) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "Number" text, "CellNumber" integer, "ReaderRFID" text, "StateId" integer, "ReaderFIO" text, "ModifierFIO" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT o.id AS "Id",
		o.date_created AS "DateCreated",
		o.number AS "Number",
		o.cell_number AS "CellNumber",
		o.reader_rfid AS "ReaderRFID",
		o.state_id AS "StateId",
		o.reader_fio AS "ReaderFIO",
		fn_fio_get(l.first_name, l.second_name, l.last_name) AS "ModifierFIO"
	FROM sk_orders o
	INNER JOIN mon_stations s ON s.id = o.safe_keeper_id
	INNER JOIN auth_logins l ON l.id = o.login_id
	WHERE s.sn = p_sn AND o.state_id BETWEEN 1 AND 3;
END;
$$;


--
-- Name: it_reader_info_get(integer, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_reader_info_get(p_id integer, p_rfidcode text, p_sn text) RETURNS TABLE("ID" integer, "DateCreated" timestamp with time zone, "Name" text, "AbisCode" text, "RFIDCode" text, "SerialNumber" text, "Email" text, "Photo" bytea)
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_rfidcode IS NULL THEN
		p_rfidcode := '-1';
	END IF;
	
	RETURN QUERY
	SELECT 
		r.id AS "ID",
		r.date_created AS "DateCreated",
		r.name AS "Name",
		r.abis_code AS "AbisCode",
		r.rfid_code AS "RFIDCode",
		r.serial_number AS "SerialNumber",
		r.email AS "Email",
		(SELECT photo FROM rdr_reader_photos WHERE is_deleted = FALSE AND reader_id = r.id LIMIT 1) AS "Photo"
	FROM rdr_readers r
	LEFT JOIN rdr_reader_photos p ON r.id = p.reader_id AND p.is_deleted = FALSE
	INNER JOIN mon_stations ms ON ms.sn = p_sn
	INNER JOIN lib_libraries ll ON ll.id = ms.library_id
	INNER JOIN owners o ON o.id = ll.owner_id
	WHERE (r.id = p_id OR r.abis_code = p_rfidcode OR r.rfid_code = p_rfidcode OR r.serial_number = p_rfidcode);

END;
$$;


--
-- Name: it_reader_info_update(text, text, text, integer, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_reader_info_update(p_rfid text, p_name text, p_email text, p_fontsizeid integer, p_langid integer, p_sn text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_owner_id UUID;
BEGIN
    -- Получаем owner_id через station → library
    SELECT lib.owner_id INTO v_owner_id
    FROM mon_stations st
    JOIN lib_libraries lib ON st.library_id = lib.id
    WHERE st.sn = p_sn
    LIMIT 1;

    IF v_owner_id IS NULL THEN
        RETURN;
    END IF;

    IF EXISTS (
        SELECT 1 
        FROM rdr_readers 
        WHERE abis_code = p_rfid AND owner_id = v_owner_id
    ) THEN
        UPDATE rdr_readers 
        SET 
            fontsize_id = p_fontsizeid,
            lang_id = p_langid,
            email = p_email,              
            name = p_name                 
        WHERE abis_code = p_rfid AND owner_id = v_owner_id;
    ELSE
        INSERT INTO rdr_readers (name, email, abis_code, lang_id, fontsize_id, owner_id)
        VALUES (p_name, p_email, p_rfid, p_langid, p_fontsizeid, v_owner_id);
    END IF;
END;
$$;


--
-- Name: it_reader_modify(integer, integer, text, text, text, text, text, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_reader_modify(p_opid integer, p_readerid integer, p_name text, p_abiscode text, p_rfidcode text, p_serialnumber text, p_email text, p_sn text, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_owner_id UUID;
    v_reader_id integer;
BEGIN
    -- Получаем owner_id по серийному номеру станции
    SELECT l.owner_id INTO v_owner_id
    FROM lib_libraries l
    INNER JOIN mon_stations s ON l.id = s.library_id
    WHERE s.sn = p_sn
    LIMIT 1;

    IF v_owner_id IS NULL THEN
        new_id := -1;
        RETURN;
    END IF;

    IF p_opid = 1 THEN
        -- Проверяем существование по кодам
        SELECT id INTO v_reader_id
        FROM rdr_readers 
        WHERE owner_id = v_owner_id 
          AND (
                abis_code = p_abiscode 
             OR rfid_code = p_rfidcode 
             OR abis_code = p_rfidcode
             OR rfid_code = p_abiscode
          )
        LIMIT 1;

        IF v_reader_id IS NULL THEN
            -- Нет дубликата — вставляем
            INSERT INTO rdr_readers (
                name, abis_code, rfid_code, serial_number, email, owner_id
            ) VALUES (
                p_name, p_abiscode, p_rfidcode, p_serialnumber, p_email, v_owner_id
            )
            RETURNING id INTO new_id;
        ELSE
            -- Дубликат найден — обновляем
            UPDATE rdr_readers
            SET 
                name = p_name,
                abis_code = p_abiscode,
                rfid_code = p_rfidcode,
                serial_number = p_serialnumber,
                email = COALESCE(p_email, email)
            WHERE id = v_reader_id;

            new_id := v_reader_id;
        END IF;

    ELSIF p_opid = 2 THEN
        -- Обновление по id
        UPDATE rdr_readers
        SET 
            rfid_code = p_rfidcode,
            abis_code = COALESCE(p_abiscode, abis_code),
            serial_number = COALESCE(p_serialnumber, serial_number),
            email = COALESCE(p_email, email),
			name = COALESCE(p_name, name)
        WHERE id = p_readerid
          AND owner_id = v_owner_id
        RETURNING id INTO new_id;

        -- Если RETURNING не сработало (нет такой записи), new_id останется NULL
        IF new_id IS NULL THEN
            new_id := -1;
        END IF;

    ELSE
        new_id := -1;  -- Неизвестная операция
    END IF;

    -- Если всё прошло, но new_id всё ещё NULL (маловероятно), ставим -1
    IF new_id IS NULL THEN
        new_id := -1;
    END IF;

    RETURN;
END;
$$;


--
-- Name: it_readers_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_readers_get(p_sn text) RETURNS TABLE("ID" integer, "DateCreated" timestamp with time zone, "Name" text, "LangID" integer, "Email" text, "AbisCode" text, "FontSizeID" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT 
		r.id AS "ID",
		r.date_created AS "DateCreated",
		r.name AS "Name",
		r.lang_id AS "LangID",
		r.email AS "Email",
		r.abis_code AS "AbisCode",
		r.fontsize_id AS "FontSizeID"
	FROM rdr_readers r
	INNER JOIN mon_stations ms ON ms.sn = p_sn
	INNER JOIN lib_libraries ll ON ll.id = ms.library_id
	WHERE ll.owner_id = r.owner_id AND r.abis_code IS NOT NULL;
END;
$$;


--
-- Name: it_safe_keeper_add(text, uuid, integer, integer, text, uuid, integer, integer, text, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_add(p_sn text, p_libraryid uuid, p_stationtypeid integer, p_safekeepertypeid integer, p_name text, p_parentid uuid, p_cellscount integer, p_cellshift integer, p_ipaddress text, p_controllerip text, INOUT new_id uuid DEFAULT NULL::uuid) RETURNS uuid
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM mon_stations WHERE sn = p_sn) THEN
        new_id := gen_random_uuid();

        INSERT INTO mon_stations (id, name, sn, station_type_id, library_id) 
        VALUES (new_id, p_name, p_sn, p_stationtypeid, p_libraryid);
        
        INSERT INTO sk_safekeepers (id, cells_count, ip_address, safe_keeper_type_id, cell_shift, parent_id, controller_ip) 
        VALUES (new_id, p_cellscount, p_ipaddress, p_safekeepertypeid, p_cellshift, p_parentid, p_controllerip);
    ELSE
        new_id := NULL; -- или можно оставить как есть (DEFAULT NULL)
    END IF;

    RETURN;
END;
$$;


--
-- Name: it_safe_keeper_camera_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_camera_get(p_sn text) RETURNS TABLE("CameraID" integer, "HardwareID" text, "IP" text, "Port" integer, "Login" text, "Password" text, "ChannelNo" integer, "CameraIP" text, "Brightness" integer, "Contrast" integer, "Sharpness" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT 
	s.device_camera_id AS "CameraID",
	d.hardware_id AS "HardwareID",
	d.ip AS "IP",
	d.port AS "Port",
	d.login AS "Login",
	d.password AS "Password",
	channel_no AS "ChannelNo",
	dc.ip AS "CameraIP",
	dc.brightness AS "Brightness",
	dc.contrast AS "Contrast",
	dc.sharpness AS "Sharpness"
	FROM mon_stations s
	INNER JOIN fd_device_cameras dc ON dc.id = s.device_camera_id
	INNER JOIN fd_devices d ON d.id = dc.device_id
	WHERE s.id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1) AND dc.is_active = TRUE
	LIMIT 1;
END;
$$;


--
-- Name: it_safe_keeper_childs_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_childs_get(p_sn text) RETURNS TABLE("ID" uuid, "DateCreated" timestamp with time zone, "Name" text, "CellsCount" integer, "SerialNumber" text, "CellsState" bigint, "ControllerIP" text, "LastDateAlive" timestamp with time zone, "IsOnline" integer, "SafeKeeperTypeID" integer, "LibraryID" uuid, "LibraryName" text, "CellShift" integer, "StorageTime" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT s.id AS "ID",
		s.date_created AS "DateCreated",
		s.name AS "Name",
		sk.cells_count AS "CellsCount",
		s.sn AS "SerialNumber",
		sk.cells_state AS "CellsState",
		sk.controller_ip AS "ControllerIP",
		COALESCE(s.last_update, '2000-01-01'::TIMESTAMP) AS "LastDateAlive",
		CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.last_update))/60, 3) < 2 THEN 1 ELSE 0 END AS "IsOnline",
		sk.safe_keeper_type_id AS "SafeKeeperTypeID",
		l.id AS "LibraryID",
		l.name AS "LibraryName",
		sk.cell_shift AS "CellShift",
		sk.storage_time AS "StorageTime"
	FROM mon_stations s
	INNER JOIN 
	sk_safekeepers sk
	ON s.id = sk.id
	INNER JOIN lib_libraries l ON l.id = s.library_id
	WHERE sk.parent_id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1) AND is_deleted = FALSE;

END;
$$;


--
-- Name: it_safe_keeper_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_get(p_sn text) RETURNS TABLE("ID" uuid, "Name" text, "CellsCount" integer, "CellsState" bigint, "SafeKeeperTypeID" integer, "CellShift" integer, "StorageTime" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    lib_id UUID;
BEGIN
    IF (p_sn LIKE '%_ALL') THEN
        p_sn := SUBSTRING(p_sn, 1, LENGTH(p_sn) - 4);
        SELECT library_id INTO lib_id 
        FROM mon_stations
        WHERE sn = p_sn
        LIMIT 1;

        RETURN QUERY
        SELECT s.id AS "ID",
            s.name AS "Name",
            sk.cells_count AS "CellsCount",
            sk.cells_state AS "CellsState",
            sk.safe_keeper_type_id AS "SafeKeeperTypeID",
            sk.cell_shift AS "CellShift",
            sk.storage_time AS "StorageTime"
        FROM mon_stations s
        INNER JOIN sk_safekeepers sk ON s.id = sk.id
        WHERE s.library_id = lib_id AND sk.is_deleted = FALSE;
    ELSE
        RETURN QUERY
        SELECT s.id AS "ID",
            s.name AS "Name",
            sk.cells_count AS "CellsCount",
            sk.cells_state AS "CellsState",
            sk.safe_keeper_type_id AS "SafeKeeperTypeID",
            sk.cell_shift AS "CellShift",
            sk.storage_time AS "StorageTime"
        FROM mon_stations s
        INNER JOIN sk_safekeepers sk ON s.id = sk.id
        WHERE s.sn = p_sn AND sk.is_deleted = FALSE;
    END IF;
END;
$$;


--
-- Name: it_safe_keeper_history_add(integer, text, integer, text, text, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_history_add(p_cellnumber integer, p_sn text, p_eventtypeid integer, p_readerrfid text, p_readerfio text, p_eventdate timestamp with time zone) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO sk_history (cell_number, safe_keeper_id, event_type_id, reader_rfid, reader_fio, date_created)
	VALUES (p_cellnumber
	,(SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1)
	,p_eventtypeid
	,p_readerrfid
	,p_readerfio
	,CURRENT_TIMESTAMP);
END;
$$;


--
-- Name: it_safe_keeper_history_by_cell_number_get(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_history_by_cell_number_get(p_sn text, p_cell integer) RETURNS TABLE("ID" integer, "DateCreated" timestamp with time zone, "SafeKeeperId" uuid, "EventTypeId" integer, "ReaderRFID" text, "ReaderFIO" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT h.id AS "ID",
		h.date_created AS "DateCreated",
		h.safe_keeper_id AS "SafeKeeperId",
		h.event_type_id AS "EventTypeId",
		h.reader_rfid AS "ReaderRFID",
		h.reader_fio AS "ReaderFIO"
	FROM sk_history h
	INNER JOIN mon_stations ms ON ms.sn = p_sn
	WHERE h.safe_keeper_id = ms.id AND h.cell_number = p_cell AND (h.event_type_id IN (1, 2, 8))
	ORDER BY h.date_created DESC
	LIMIT 5;
END;
$$;


--
-- Name: it_safe_keeper_history_master_add(integer, text, integer, integer, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_history_master_add(p_cellnumber integer, p_sn text, p_eventtypeid integer, p_masterrfidid integer, p_eventdate timestamp with time zone) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO sk_history_master (cell_number, safe_keeper_id, event_type_id, master_rfid_id, master_rfid_fio, date_created)
	VALUES (p_cellnumber
	,(SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1)
	,p_eventtypeid
	,p_masterrfidid
	,(SELECT fn_fio_get(first_name, last_name, second_name) FROM sk_masters_rfid WHERE id = p_masterrfidid LIMIT 1)
	,CURRENT_TIMESTAMP);
END;
$$;


--
-- Name: it_safe_keeper_library_modify(text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_library_modify(p_sn text, p_libraryid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    safekeeper_id UUID;
BEGIN
    SELECT id INTO safekeeper_id 
    FROM mon_stations 
    WHERE sn = p_sn
    LIMIT 1;

    UPDATE mon_stations
    SET library_id = p_libraryid
    WHERE id IN (
        SELECT id 
        FROM sk_safekeepers 
        WHERE id = safekeeper_id OR parent_id = safekeeper_id
    );
END;
$$;


--
-- Name: it_safe_keeper_masters_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_masters_get(p_sn text) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "RFID" text, "FirstName" text, "SecondName" text, "LastName" text, "IsDeleted" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT m.id AS "Id",
		m.date_created AS "DateCreated",
		m.rfid AS "RFID",
		m.first_name AS "FirstName",
		m.second_name AS "SecondName",
		m.last_name AS "LastName",
		m.is_deleted AS "IsDeleted"
	FROM sk_masters_rfid m
	INNER JOIN sk_safekeepers_masters_rfid mr ON m.id = mr.master_rfid_id
	INNER JOIN mon_stations s ON s.id = mr.safe_keeper_id
	WHERE s.sn = p_sn;
END;
$$;


--
-- Name: it_safe_keeper_modify(integer, text, integer, integer, text, text, integer, integer, uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_modify(p_opid integer, p_name text, p_cellscount integer, p_cellshift integer, p_sn text, p_ipaddress text, p_stationtypeid integer, p_safekeepertypeid integer, p_libraryid uuid, INOUT new_id uuid DEFAULT NULL::uuid) RETURNS uuid
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_id uuid;
BEGIN
    IF p_opid = 1 THEN
        IF NOT EXISTS (SELECT 1 FROM mon_stations WHERE sn = p_sn) THEN
            new_id := gen_random_uuid();

            INSERT INTO mon_stations (id, name, sn, station_type_id, library_id) 
            VALUES (new_id, p_name, p_sn, p_stationtypeid, p_libraryid);
            
            INSERT INTO sk_safekeepers (id, cells_count, ip_address, safe_keeper_type_id, cell_shift) 
            VALUES (new_id, p_cellscount, p_ipaddress, p_safekeepertypeid, p_cellshift);
        END IF;
        -- Если запись существует, new_id остаётся NULL

    ELSIF p_opid = 2 THEN
        -- Обновляем станцию
        UPDATE mon_stations
        SET name = p_name,
            station_type_id = p_stationtypeid
        WHERE sn = p_sn;

        -- Обновляем сейф-кипер
        UPDATE sk_safekeepers
        SET cells_count = p_cellscount,
            ip_address = p_ipaddress,
            safe_keeper_type_id = p_safekeepertypeid,
            cell_shift = p_cellshift  -- добавлено для согласованности
        WHERE id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1);

        -- Получаем и возвращаем существующий ID
        SELECT id INTO new_id
        FROM mon_stations
        WHERE sn = p_sn
        LIMIT 1;
    END IF;

    RETURN;
END;
$$;


--
-- Name: it_safe_keeper_return_bodies_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_return_bodies_get(p_sn text) RETURNS TABLE("ReturnID" integer, "BookCode" text, "BookName" text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id UUID;
BEGIN
    SELECT id INTO v_station_id 
    FROM mon_stations 
    WHERE sn = p_sn 
    LIMIT 1;

    IF v_station_id IS NULL THEN
        RETURN;
    END IF;

    RETURN QUERY
    SELECT 
        rb.return_id AS "ReturnID",
        b.book_code AS "BookCode",
        b.book_name AS "BookName"
    FROM sk_returns_body rb
    INNER JOIN sk_returns r ON r.id = rb.return_id
    INNER JOIN lib_books b ON b.id = rb.book_id::integer 
    WHERE r.station_id = v_station_id 
      AND r.date_closed IS NULL;
END;
$$;


--
-- Name: it_safe_keeper_return_body_add(text, text, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_return_body_add(p_sn text, p_bookcode text, p_bookname text, p_returnid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id UUID;
    v_book_id INTEGER;
    v_library_id UUID;
    v_owner_id UUID;
BEGIN
    -- Получаем station_id, library_id, owner_id
    SELECT id, library_id INTO v_station_id, v_library_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_library_id IS NULL THEN
        RETURN; -- Станция не найдена или нет library_id
    END IF;

    SELECT owner_id INTO v_owner_id
    FROM lib_libraries
    WHERE id = v_library_id
    LIMIT 1;

    IF v_owner_id IS NULL THEN
        RETURN; -- Библиотека без owner_id
    END IF;

    -- Проверяем, существует ли книга в этой библиотеке
    SELECT id INTO v_book_id
    FROM lib_books
    WHERE book_code = p_bookcode
      AND library_id = v_library_id  -- ← переменная с префиксом!
    LIMIT 1;

    IF v_book_id IS NULL THEN
        -- Книги нет в этой библиотеке — пробуем создать или взять существующую
        -- Сначала проверим, есть ли вообще такая книга (в любой библиотеке)
        SELECT id INTO v_book_id
        FROM lib_books
        WHERE book_code = p_bookcode
        LIMIT 1;

        IF v_book_id IS NULL THEN
            -- Нет нигде — создаём в текущей библиотеке
            INSERT INTO lib_books (book_code, book_name, library_id)
            VALUES (p_bookcode, p_bookname, v_library_id)
            RETURNING id INTO v_book_id;
        ELSE
            INSERT INTO lib_books (book_code, book_name, library_id)
            VALUES (p_bookcode, p_bookname, v_library_id)
            RETURNING id INTO v_book_id;
        END IF;
    END IF;

    -- Добавляем в тело возврата
    INSERT INTO sk_returns_body (book_id, return_id)
    VALUES (v_book_id, p_returnid);
END;
$$;


--
-- Name: it_safe_keeper_return_modify(text, integer, integer, text, text, text, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_return_modify(p_sn text, p_return_id integer, p_cell_number integer, p_patron_code text, p_patron_name text, p_master_code text, p_owner_id uuid, INOUT new_id integer DEFAULT NULL::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id       uuid;
    v_library_id       uuid;
    v_owner_id         uuid;
    v_patron_id        integer;
    v_master_id        integer;
    v_master_name      text;
BEGIN
    -- === 1. Проверка входных данных ===
    IF p_sn IS NULL OR trim(p_sn) = '' THEN
        RAISE EXCEPTION 'Station serial number (SN) is required';
    END IF;

    -- === 2. Получаем station, library и owner ===
    SELECT s.id, s.library_id INTO v_station_id, v_library_id
    FROM mon_stations s
    WHERE s.sn = p_sn
    LIMIT 1;

    IF v_station_id IS NULL THEN
        RAISE EXCEPTION 'Station with SN % not found', p_sn;
    END IF;

    SELECT l.owner_id INTO v_owner_id
    FROM lib_libraries l
    WHERE l.id = v_library_id
    LIMIT 1;

    IF v_owner_id IS NULL THEN
        RAISE EXCEPTION 'Owner not found for station %', p_sn;
    END IF;

    -- === 3. Ветка: создание нового возврата (p_return_id IS NULL) ===
    IF p_return_id IS NULL THEN
        -- Найти или создать читателя
        IF NOT EXISTS (
            SELECT 1 FROM rdr_readers 
            WHERE abis_code = p_patron_code AND owner_id = v_owner_id
        ) THEN
            SELECT it_reader_modify(
                p_opid => 1,
                p_readerid => NULL,
                p_name => p_patron_name,
                p_abiscode => p_patron_code,
                p_rfidcode => NULL,
                p_serialnumber => NULL,
                p_email => NULL,
                p_sn => p_sn,
                new_id => NULL
            ) INTO v_patron_id;
        ELSE
            SELECT id INTO v_patron_id
            FROM rdr_readers
            WHERE abis_code = p_patron_code AND owner_id = v_owner_id
            LIMIT 1;
        END IF;

        IF v_patron_id IS NULL OR v_patron_id <= 0 THEN
            RAISE EXCEPTION 'Failed to obtain valid patron ID for code %', p_patron_code;
        END IF;

        -- Создать возврат
        INSERT INTO sk_returns (station_id, cell_number, patron_id)
        VALUES (v_station_id, p_cell_number, v_patron_id)
        RETURNING id INTO new_id;

        -- Обновить состояние ячейки
        UPDATE sk_safekeepers
        SET cells_state = cells_state | (1 << (p_cell_number - 1))
        WHERE id = v_station_id;

        -- Запись в историю (читатель)
        INSERT INTO sk_history (cell_number, safe_keeper_id, event_type_id, reader_rfid, reader_fio)
        VALUES (p_cell_number, v_station_id, 9, p_patron_code, p_patron_name);

    -- === 4. Ветка: закрытие существующего возврата (p_return_id NOT NULL) ===
    ELSE
        -- Проверка обязательного параметра
        IF p_master_code IS NULL OR trim(p_master_code) = '' THEN
            RAISE EXCEPTION 'Master RFID code is required to close return';
        END IF;

        -- Найти мастера
        SELECT id INTO v_master_id
        FROM sk_masters_rfid
        WHERE rfid = p_master_code AND owner_id = v_owner_id
        LIMIT 1;

        IF v_master_id IS NULL THEN
            RAISE EXCEPTION 'Master with RFID % not found for owner %', p_master_code, v_owner_id;
        END IF;

        -- Получить ФИО мастера
        SELECT CONCAT_WS(' ', first_name, last_name, second_name) INTO v_master_name
        FROM sk_masters_rfid
        WHERE id = v_master_id
        LIMIT 1;

        -- Закрыть возврат
        UPDATE sk_returns
        SET date_closed = CURRENT_TIMESTAMP
        WHERE id = p_return_id;

        new_id := p_return_id;

        -- Освободить ячейку
        UPDATE sk_safekeepers
        SET cells_state = cells_state & ~(1 << (p_cell_number - 1))
        WHERE id = v_station_id;

        -- Запись в историю мастера (обязательно: master_rfid_id NOT NULL)
        INSERT INTO sk_history_master (cell_number, safe_keeper_id, event_type_id, master_rfid_id, master_rfid_fio)
        VALUES (p_cell_number, v_station_id, 10, v_master_id, v_master_name);
    END IF;
END;
$$;


--
-- Name: it_safe_keeper_returns_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keeper_returns_get(p_sn text) RETURNS TABLE("ReturnID" integer, "DateCreated" timestamp with time zone, "CellNumber" integer, "PatronName" text, "PatronCode" text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id UUID;
BEGIN
    SELECT id INTO v_station_id 
    FROM mon_stations 
    WHERE sn = p_sn 
    LIMIT 1;

    IF v_station_id IS NULL THEN
        RETURN; -- Станция не найдена — возвращаем пустой результат
    END IF;

    RETURN QUERY
    SELECT
        r.id AS "ReturnID",
        r.date_created AS "DateCreated",
        r.cell_number AS "CellNumber",
        rdr.name AS "PatronName",
        rdr.abis_code AS "PatronCode"
    FROM sk_returns r
    INNER JOIN rdr_readers rdr ON rdr.id = r.patron_id
    WHERE r.station_id = v_station_id 
      AND r.date_closed IS NULL;
END;
$$;


--
-- Name: it_safe_keepers_count_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_safe_keepers_count_get(p_sn text) RETURNS TABLE("Count" integer)
    LANGUAGE plpgsql STABLE
    AS $$
BEGIN
    RETURN QUERY
    SELECT COUNT(*)::integer AS "Count"
    FROM mon_stations s
    INNER JOIN sk_safekeepers sk ON s.id = sk.id
    WHERE sk.is_deleted = FALSE 
      AND sk.parent_id IS NULL 
      AND s.library_id IN (
          SELECT library_id 
          FROM mon_stations 
          WHERE sn = p_sn
      );
END;
$$;


--
-- Name: it_smart_shelf_all_books_offline_set(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_smart_shelf_all_books_offline_set(p_sn text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	UPDATE mon_book_shelves_stat
	SET is_online = FALSE
	WHERE station_id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1);
END;
$$;


--
-- Name: it_smart_shelf_count_takes_reset(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_smart_shelf_count_takes_reset(p_sn text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	UPDATE mon_book_shelves_stat
	SET count_takes = 0
	WHERE station_id = (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1);
END;
$$;


--
-- Name: it_special_books_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_special_books_get(p_sn text) RETURNS TABLE("ID" integer, "BookCode" text, "BookName" text, "AuthorsName" text, "Description" text, "Cover" bytea)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_library_id UUID;
BEGIN
    SELECT ms.library_id INTO v_library_id
    FROM mon_stations ms
    WHERE ms.sn = p_sn
    LIMIT 1;

    RETURN QUERY
    SELECT b.id AS "ID",
           b.book_code AS "BookCode",
           b.book_name AS "BookName",
           b.authors_name AS "AuthorsName",
           b.description AS "Description",
           bc.cover AS "Cover"
    FROM lib_books b
    LEFT JOIN lib_books_covers bc ON b.id = bc.book_id
    WHERE b.is_book = 1
      AND b.is_special = true
      AND b.library_id = v_library_id;  -- теперь ссылка на переменную однозначна
END;
$$;


--
-- Name: it_station_add(text, uuid, integer, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_add(p_sn text, p_libraryid uuid, p_typeid integer, p_name text, INOUT new_id uuid DEFAULT NULL::uuid) RETURNS uuid
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM mon_stations WHERE sn = p_sn) THEN
		INSERT INTO mon_stations (name, station_type_id, sn, library_id)
		VALUES (p_name, p_typeid, p_sn, p_libraryid)
		RETURNING id INTO new_id;
	END IF;
END;
$$;


--
-- Name: it_station_config_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_config_get(p_sn text) RETURNS TABLE("Config" text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id UUID;
BEGIN
    SELECT id INTO v_station_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    RETURN QUERY
    SELECT config AS "Config"
    FROM mon_stations_config
    WHERE station_id = v_station_id;  -- однозначная ссылка на переменную
END;
$$;


--
-- Name: it_station_config_set(text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_config_set(p_sn text, p_config text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_id UUID;
BEGIN
    SELECT id INTO v_station_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF NOT EXISTS (SELECT 1 FROM mon_stations_config WHERE station_id = v_station_id) THEN
        INSERT INTO mon_stations_config (station_id, config)
        VALUES (v_station_id, p_config);
    ELSE
        UPDATE mon_stations_config
        SET config = p_config
        WHERE station_id = v_station_id;
    END IF;
END;
$$;


--
-- Name: it_station_event_add(text, integer, text, text, integer, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_event_add(p_sn text, p_eventid integer, p_usercode text, p_message text, p_count integer, p_ishourtype boolean) RETURNS void
    LANGUAGE plpgsql
    AS $_$
DECLARE
    v_rec_id INTEGER;
    v_station_id UUID;
    v_inserted_id INTEGER;
    v_pair_id INTEGER;
    v_library_id UUID;
BEGIN
    -- Получаем station_id
    SELECT id INTO v_station_id 
    FROM mon_stations 
    WHERE sn = p_sn
    LIMIT 1;

    IF v_station_id IS NULL THEN
        RETURN;
    END IF;

    -- Проверка IsSingleEvent
    IF (SELECT is_single_event FROM mon_events WHERE id = p_eventid) = 0 THEN
        SELECT id INTO v_rec_id 
        FROM mon_stations_event_history
        WHERE event_id = p_eventid 
          AND station_id = v_station_id 
          AND DATE_TRUNC('hour', date_event) = DATE_TRUNC('hour', CURRENT_TIMESTAMP)
        LIMIT 1;

        IF v_rec_id IS NOT NULL THEN
            UPDATE mon_stations_event_history 
            SET count = count + 1 
            WHERE id = v_rec_id;
            v_inserted_id := v_rec_id;
        ELSE
            INSERT INTO mon_stations_event_history (station_id, date_event, event_id, message, count, user_code)
            VALUES (v_station_id, CURRENT_TIMESTAMP, p_eventid, p_message, 1, p_usercode);
            v_inserted_id := LASTVAL();
        END IF;
    ELSE
        INSERT INTO mon_stations_event_history (station_id, date_event, event_id, message, count, user_code)
        VALUES (v_station_id, CURRENT_TIMESTAMP, p_eventid, p_message, 1, p_usercode);
        v_inserted_id := LASTVAL();
    END IF;

    -- === ЕДИНСТВЕННОЕ МЕСТО ВЫЗОВА КЭШИРОВАНИЯ ===
    -- Только если p_message выглядит как код книги (не пустой, не системное значение)
    IF p_message IS NOT NULL 
       AND p_message <> '' 
       AND p_message !~ '^[0-9]{1,3}$'  -- исключаем числа вроде "27", "30" — это ID событий!
    THEN
        -- Проверяем, существует ли такая книга
        IF EXISTS (
            SELECT 1 
            FROM lib_books 
            WHERE book_code = p_message
        ) THEN
            PERFORM it_books_cache_add_update(p_sn, p_message);
        END IF;
    END IF;

    -- Логика пары событий (Начало/Конец сессии)
    IF p_eventid = 26 THEN  -- Конец сессии
        SELECT id INTO v_pair_id
        FROM mon_stations_event_history
        WHERE station_id = v_station_id 
          AND event_id = 23 
          AND user_code = p_usercode
        ORDER BY date_event DESC
        LIMIT 1;

        IF v_pair_id > 0 THEN
            UPDATE mon_stations_event_history
            SET pair_id = v_pair_id  
            WHERE id = v_inserted_id;
        END IF;
    END IF;
END;
$_$;


--
-- Name: it_station_info_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_info_get(p_sn text) RETURNS TABLE("ID" uuid, "StationTypeID" integer, "Name" text, "LibraryID" uuid, "LibraryName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT s.id AS "ID",
		s.station_type_id AS "StationTypeID",
		s.name AS "Name",
		s.library_id AS "LibraryID",
		l.name AS "LibraryName"
	FROM mon_stations s
	INNER JOIN lib_libraries l ON l.id = s.library_id
	WHERE s.sn = p_sn;
END;
$$;


--
-- Name: it_station_modify(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_modify(p_id uuid, p_name text, p_libraryid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	UPDATE mon_stations
	SET name = p_name,
		library_id = p_libraryid
	WHERE id = p_id;
END;
$$;


--
-- Name: it_station_states_update(text, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_states_update(p_sn text, p_part1 boolean, p_part2 boolean, p_part3 boolean, p_part4 boolean, p_part5 boolean, p_part6 boolean, p_part7 boolean, p_part8 boolean, p_part9 boolean, p_part10 boolean DEFAULT false, p_part11 boolean DEFAULT false, p_ip text DEFAULT ''::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    station_id UUID;
    station_type INTEGER;
BEGIN
    SELECT id, station_type_id INTO station_id, station_type
    FROM mon_stations WHERE sn = p_sn
    LIMIT 1;

    UPDATE mon_stations 
    SET last_update = CURRENT_TIMESTAMP,
        monitor_part1 = CASE WHEN p_part1 THEN CURRENT_TIMESTAMP ELSE monitor_part1 END,
        monitor_part2 = CASE WHEN p_part2 THEN CURRENT_TIMESTAMP ELSE monitor_part2 END,
        monitor_part3 = CASE WHEN p_part3 THEN CURRENT_TIMESTAMP ELSE monitor_part3 END,
        monitor_part4 = CASE WHEN p_part4 THEN CURRENT_TIMESTAMP ELSE monitor_part4 END,
        monitor_part5 = CASE WHEN p_part5 THEN CURRENT_TIMESTAMP ELSE monitor_part5 END,
        monitor_part6 = CASE WHEN p_part6 THEN CURRENT_TIMESTAMP ELSE monitor_part6 END,
        monitor_part7 = CASE WHEN p_part7 THEN CURRENT_TIMESTAMP ELSE monitor_part7 END,
        monitor_part8 = CASE WHEN p_part8 THEN CURRENT_TIMESTAMP ELSE monitor_part8 END,
        monitor_part9 = CASE WHEN p_part9 THEN CURRENT_TIMESTAMP ELSE monitor_part9 END,
        monitor_part10 = CASE WHEN p_part10 THEN CURRENT_TIMESTAMP ELSE monitor_part10 END,
        monitor_part11 = CASE WHEN p_part11 THEN CURRENT_TIMESTAMP ELSE monitor_part11 END,
        ip = p_ip
    WHERE id = station_id;

    INSERT INTO mon_stations_monitoring (station_id)
    VALUES (station_id);

    IF (station_type = 4) THEN
        UPDATE mon_stations 
        SET last_update = CURRENT_TIMESTAMP 
        WHERE id IN (SELECT id FROM sk_safekeepers WHERE parent_id = station_id);
    END IF;

    IF station_type <> 4 THEN
        IF p_part1 = TRUE THEN
            PERFORM it_station_event_add(p_sn, 30, '', '', 1, TRUE);  --RFID-считыватель подключен
        ELSE
            PERFORM it_station_event_add(p_sn, 27, '', '', 1, TRUE);  --Ошибка RFID-считывателя
        END IF;
    END IF;

    IF p_part2 = TRUE THEN
        PERFORM it_station_event_add(p_sn, 1, '', '', 1, TRUE);  --RFID-кардридер подключен
    ELSE
        PERFORM it_station_event_add(p_sn, 33, '', '', 1, TRUE);  --Ошибка RFID кардридера
    END IF;

    IF station_type <> 4 THEN
        IF p_part3 = TRUE THEN
            PERFORM it_station_event_add(p_sn, 42, '', '', 1, TRUE);  --Принтер подключен
        ELSE
            PERFORM it_station_event_add(p_sn, 43, '', '', 1, TRUE);  --Ошибка принтера
        END IF;
    END IF;

    IF p_part4 = TRUE THEN
        PERFORM it_station_event_add(p_sn, 31, '', '', 1, TRUE);  --АБИС подключено
    ELSE
        PERFORM it_station_event_add(p_sn, 28, '', '', 1, TRUE);  --Ошибка подключения к АБИС
    END IF;

    IF station_type IN (2, 4, 5, 7, 9) THEN
        IF p_part5 = TRUE THEN
            PERFORM it_station_event_add(p_sn, 32, '', '', 1, TRUE);  --Контроллер подключен
        ELSE
            PERFORM it_station_event_add(p_sn, 29, '', '', 1, TRUE);  --Ошибка подключения к контроллеру
        END IF;
    END IF;

    IF station_type = 3 THEN
        IF p_part7 = TRUE THEN
            PERFORM it_station_event_add(p_sn, 4, '', '', 1, TRUE);  --Ридер умной полки подключен
        ELSE
            PERFORM it_station_event_add(p_sn, 40, '', '', 1, TRUE);  --Ошибка подключения к ридеру умной полки
        END IF;
    END IF;
END;
$$;


--
-- Name: it_station_type_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_station_type_get(p_sn text) RETURNS TABLE("StationTypeID" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT station_type_id AS "StationTypeID" FROM mon_stations WHERE sn = p_sn LIMIT 1;
END;
$$;


--
-- Name: it_stations_banners_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_stations_banners_get(p_sn text) RETURNS TABLE("ID" integer, "Name" text, "DateCreated" timestamp with time zone, "PeriodFrom" timestamp with time zone, "PeriodTo" timestamp with time zone, "Interval" integer, "Image" bytea)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		sb.id AS "ID",
		sb.name AS "Name",
		sb.date_created AS "DateCreated",
		sb.period_from AS "PeriodFrom",
		sb.period_to AS "PeriodTo",
		sb.interval AS "Interval",
		sb.image AS "Image"
	FROM mon_stations_banners sb
	INNER JOIN mon_stations_banners_tasks sbt ON sbt.banner_id = sb.id
	INNER JOIN mon_stations s ON s.id = sbt.station_id
	WHERE s.sn = p_sn AND sb.is_deleted = FALSE AND sb.is_enabled = TRUE AND sb.period_to >= CURRENT_TIMESTAMP;
END;
$$;


--
-- Name: it_stations_with_coordinates_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_stations_with_coordinates_get(p_sn text) RETURNS TABLE("StationID" uuid, "StationName" text, "StationTypeID" integer, "SN" text, "Coordinates" text, "IP" text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_library_id UUID;
BEGIN
    SELECT library_id INTO v_library_id 
    FROM mon_stations 
    WHERE sn = p_sn 
    LIMIT 1;

    RETURN QUERY
    SELECT id AS "StationID",
           name AS "StationName",
           station_type_id AS "StationTypeID",
           sn AS "SN",
           coordinates AS "Coordinates",
           ip AS "IP"
    FROM mon_stations
    WHERE library_id = v_library_id
      AND coordinates IS NOT NULL;
END;
$$;


--
-- Name: it_storage_occupied_cells_by_library_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_storage_occupied_cells_by_library_get(p_sn text) RETURNS TABLE("ID" integer, "ReaderRFID" text, "CellNo" integer, "SafeKeeperId" uuid, "SafeKeeperName" text, "SafeKeeperTypeID" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_library_id UUID;
BEGIN
    SELECT library_id INTO v_library_id 
    FROM mon_stations 
    WHERE sn = p_sn 
    LIMIT 1;

    RETURN QUERY
    SELECT 
        sc.id AS "ID",
        sc.reader_rfid AS "ReaderRFID",
        sc.cell_no AS "CellNo",
        sc.safe_keeper_id AS "SafeKeeperId",
        st.name AS "SafeKeeperName",
        sk.safe_keeper_type_id AS "SafeKeeperTypeID"
    FROM sk_storage_cells sc
    INNER JOIN sk_safekeepers sk ON sk.id = sc.safe_keeper_id
    INNER JOIN mon_stations st ON sk.id = st.id
    WHERE st.library_id = v_library_id 
      AND sc.date_close IS NULL;
END;
$$;


--
-- Name: it_storage_occupied_cells_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_storage_occupied_cells_get(p_sn text) RETURNS TABLE("ID" integer, "ReaderRFID" text, "ReaderFIO" text, "DateOpen" timestamp with time zone, "CellNo" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT sc.id AS "ID",
		sc.reader_rfid AS "ReaderRFID",
		sc.reader_fio AS "ReaderFIO",
		sc.date_open AS "DateOpen",
		sc.cell_no AS "CellNo"
	FROM sk_storage_cells sc
	INNER JOIN mon_stations s ON s.id = sc.safe_keeper_id
	WHERE s.sn = p_sn AND sc.date_close IS NULL;
END;
$$;


--
-- Name: it_storage_pickup_things(text, integer, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_storage_pickup_things(p_sn text, p_recid integer, p_dateclose timestamp with time zone) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_cell_no INTEGER;
    v_keeper_id UUID;
BEGIN
    SELECT id INTO v_keeper_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_keeper_id IS NULL THEN
        RETURN;
    END IF;

    SELECT cell_no INTO v_cell_no 
    FROM sk_storage_cells
    WHERE safe_keeper_id = v_keeper_id AND id = p_recid
    LIMIT 1;

    IF v_cell_no IS NOT NULL THEN
        UPDATE sk_storage_cells
        SET date_close = p_dateclose  -- используем переданный параметр
        WHERE safe_keeper_id = v_keeper_id AND id = p_recid;

        -- Освобождаем ячейку: сбрасываем бит в 0
        UPDATE sk_safekeepers
        SET cells_state = cells_state & ~(1::BIGINT << (v_cell_no - 1))
        WHERE id = v_keeper_id;
    END IF;
END;
$$;


--
-- Name: it_storage_put_things(text, integer, text, text, timestamp with time zone, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_storage_put_things(p_sn text, p_cellno integer, p_readerrfid text, p_readerfio text, p_dateopen timestamp with time zone, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_cell_state BIGINT;
    v_bit_mask BIGINT;
    v_keeper_id UUID;
BEGIN
    -- Получаем ID шкафчика один раз
    SELECT id INTO v_keeper_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_keeper_id IS NULL THEN
        new_id := -1;
        RETURN;
    END IF;

    -- Получаем текущее состояние ячеек
    SELECT cells_state INTO v_cell_state
    FROM sk_safekeepers
    WHERE id = v_keeper_id;

    -- Формируем битовую маску для указанной ячейки
    v_bit_mask := 1::BIGINT << (p_cellno - 1);

    -- Проверяем, занята ли ячейка (бит = 1 — занята)
    IF (v_cell_state & v_bit_mask) = v_bit_mask THEN
        new_id := -100; -- ячейка занята
    ELSE
        -- Занимаем ячейку: устанавливаем бит в 1
        UPDATE sk_safekeepers
        SET cells_state = cells_state | v_bit_mask
        WHERE id = v_keeper_id;

        -- Добавляем запись в sk_storage_cells
        INSERT INTO sk_storage_cells (reader_rfid, reader_fio, cell_no, safe_keeper_id, date_open)
        VALUES (p_readerrfid, p_readerfio, p_cellno, v_keeper_id, CURRENT_TIMESTAMP)
        RETURNING id INTO new_id;
    END IF;
END;
$$;


--
-- Name: it_vending_books_get(text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_vending_books_get(p_sn text) RETURNS TABLE("ID" integer, "VendingStationID" uuid, "CellNumber" integer, "BookCode" text, "BookName" text, "MasterID" integer, "DateIn" timestamp with time zone, "VisualCellNo" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    stationid UUID;
BEGIN
    stationid := (SELECT id FROM mon_stations WHERE sn = p_sn LIMIT 1);
    RETURN QUERY
    SELECT vb.id AS "ID",
        vb.vending_station_id AS "VendingStationID",
        vb.cell_number AS "CellNumber",
        vb.book_code AS "BookCode",
        vb.book_name AS "BookName",
        vb.master_id AS "MasterID",
        vb.date_in AS "DateIn",
        vb.cell_number + sk.cell_shift AS "VisualCellNo"
    FROM sk_vending_books vb
    INNER JOIN sk_safekeepers sk ON sk.id = vb.vending_station_id
    WHERE (vb.vending_station_id = stationid OR sk.parent_id = stationid) AND vb.date_out IS NULL;
END;
$$;


--
-- Name: it_vending_cancel(text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_vending_cancel(p_sn text, p_recid integer, p_masterid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_cell_no INTEGER;
    v_station_id UUID;
BEGIN
    SELECT id INTO v_station_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_station_id IS NULL THEN
        RETURN;
    END IF;

    SELECT cell_number INTO v_cell_no
    FROM sk_vending_books
    WHERE vending_station_id = v_station_id AND id = p_recid;

    IF v_cell_no IS NOT NULL THEN
        -- Освобождаем ячейку: сбрасываем бит в 0
        UPDATE sk_safekeepers
        SET cells_state = cells_state & ~(1::BIGINT << (v_cell_no - 1))
        WHERE id = v_station_id;

        DELETE FROM sk_vending_books WHERE id = p_recid;
    END IF;
END;
$$;


--
-- Name: it_vending_put_book(text, integer, text, text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_vending_put_book(p_sn text, p_cellno integer, p_bookcode text, p_bookname text, p_masterid integer, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_cell_state BIGINT;
    v_power BIGINT;
    v_station_id UUID;
    v_library_id UUID;
BEGIN
    -- Находим станцию
    SELECT s.id,
           (SELECT cells_state FROM sk_safekeepers WHERE id = s.id),
           s.library_id
    INTO v_station_id, v_cell_state, v_library_id
    FROM mon_stations s
    WHERE s.sn = p_sn
    LIMIT 1;

    v_power := POWER(2::BIGINT, p_cellno - 1);

    -- Проверка занятости ячейки
    IF (v_cell_state & v_power) = v_power THEN
        new_id := -100;
    ELSE
        -- Обновляем состояние ячеек
        UPDATE sk_safekeepers
        SET cells_state = cells_state | v_power
        WHERE id = v_station_id;

        -- Добавляем книгу в автоматы
        INSERT INTO sk_vending_books (vending_station_id, cell_number, book_code, book_name, master_id)
        VALUES (v_station_id, p_cellno, p_bookcode, p_bookname, p_masterid)
        RETURNING id INTO new_id;

        -- Добавляем книгу в библиотеку, если ещё не существует
        IF NOT EXISTS (
            SELECT 1 FROM lib_books b 
            WHERE b.book_code = p_bookcode AND b.library_id = v_library_id
        ) THEN
            INSERT INTO lib_books (book_code, book_name, library_id)
            VALUES (p_bookcode, p_bookname, v_library_id);
        END IF;
    END IF;
END;
$$;


--
-- Name: it_vending_take_book(text, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_vending_take_book(p_sn text, p_recid integer, p_usercode text) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_cell_no INTEGER;
    v_station_id UUID;
BEGIN
    SELECT id INTO v_station_id
    FROM mon_stations
    WHERE sn = p_sn
    LIMIT 1;

    IF v_station_id IS NULL THEN
        RETURN;
    END IF;

    SELECT cell_number INTO v_cell_no
    FROM sk_vending_books
    WHERE vending_station_id = v_station_id AND id = p_recid
    LIMIT 1;

    IF v_cell_no IS NOT NULL THEN
        -- Освобождаем ячейку: сбрасываем бит в 0
        UPDATE sk_safekeepers
        SET cells_state = cells_state & ~(1::BIGINT << (v_cell_no - 1))
        WHERE id = v_station_id;

        UPDATE sk_vending_books
        SET date_out = CURRENT_TIMESTAMP,
            user_code = p_usercode
        WHERE id = p_recid;
    END IF;
END;
$$;


--
-- Name: it_visitor_photo_add(integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, integer, bytea, bytea); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.it_visitor_photo_add(p_cameraid integer, p_readerid integer, p_age integer, p_attraction integer, p_beard integer, p_emotion integer, p_eyes integer, p_glasses integer, p_mask integer, p_mouth integer, p_similarity integer, p_race integer, p_sex integer, p_photo bytea, p_fullcadre bytea) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_id INT;
BEGIN
    BEGIN
        INSERT INTO fd_visitors_photo(camera_id, reader_id, photo)
        VALUES(p_cameraid, p_readerid, p_photo)
        RETURNING ID INTO v_id;
        
        IF v_id IS NOT NULL THEN
            INSERT INTO fd_visitors_full_cadre(visitor_photo_id, full_cadre)
            VALUES(v_id, p_fullcadre);
            
            INSERT INTO fd_visitors_values(visitor_photo_id, age, attraction, beard, emotion, eyes, glasses, mask, mouth, similarity, race, sex)
            VALUES(v_id, p_age, p_attraction, p_beard, p_emotion, p_eyes, p_glasses, p_mask, p_mouth, p_similarity, p_race, p_sex);
        END IF;
        
    EXCEPTION
        WHEN OTHERS THEN
            RAISE;
    END;
END;
$$;


--
-- Name: mob_books_cache_add_update(uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_books_cache_add_update(p_libraryid uuid, p_books text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_libraryid IS NOT NULL THEN
		-- Проверяем, есть ли книги для вставки
		IF NOT EXISTS (
			SELECT 1 
			FROM bk_books_cache 
			WHERE book_code IN (SELECT unnest(string_to_array(p_books, ';'))) 
			  AND library_id = p_libraryid
		)
		AND EXISTS (
			SELECT 1 
			FROM bk_books bb 
			INNER JOIN lib_books b ON b.id = bb.book_id 
			WHERE b.book_code IN (SELECT unnest(string_to_array(p_books, ';')))
			  AND b.library_id = p_libraryid
		)
		THEN
			-- Вставляем новые записи
			INSERT INTO bk_books_cache (book_code, library_id, date_last_update)
			SELECT DISTINCT unnest(string_to_array(p_books, ';')), p_libraryid, CURRENT_TIMESTAMP
			WHERE unnest(string_to_array(p_books, ';')) IN (
				SELECT b.book_code 
				FROM bk_books bb 
				INNER JOIN lib_books b ON b.id = bb.book_id 
				WHERE b.library_id = p_libraryid
			);
		ELSE
			-- Обновляем существующие записи
			UPDATE bk_books_cache
			SET date_last_update = CURRENT_TIMESTAMP,
				is_checked = FALSE
			WHERE book_code IN (SELECT unnest(string_to_array(p_books, ';'))) 
			  AND library_id = p_libraryid;
		END IF;
	END IF;
END;
$$;


--
-- Name: mob_kpi_cameras_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_cameras_get(p_library_id uuid) RETURNS TABLE("CameraID" integer, "CameraName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        dc.id AS "CameraID",
        dc.name AS "CameraName"
    FROM fd_device_cameras dc 
    INNER JOIN fd_devices d ON d.id = dc.device_id
    WHERE d.library_id = p_library_id
    ORDER BY dc.name;
END;
$$;


--
-- Name: mob_kpi_dashboard_get(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_dashboard_get(p_login_id integer) RETURNS TABLE("HourValue" integer, "PatronsPerHourCount" integer, "ServicesPerHourCount" integer, "PatronsToday" integer, "ServicesToday" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    today_date date;
BEGIN
    today_date := CURRENT_DATE;
    
    RETURN QUERY
    WITH hourly_data AS (
        SELECT
            hd.hour_value,
            COUNT(DISTINCT sh.reader_id)::integer AS patrons_per_hour_count,
            COUNT(DISTINCT sh.service_id)::integer AS services_per_hour_count
        FROM dir_hours_day hd
        LEFT JOIN kpi_service_history sh ON sh.login_id = p_login_id 
            AND hd.hour_value = EXTRACT(HOUR FROM sh.date_created) 
            AND sh.date_created::date = today_date
        GROUP BY hd.hour_value
    ),
    daily_totals AS (
        SELECT
            COUNT(DISTINCT reader_id)::integer AS patrons_today,
            COUNT(DISTINCT service_id)::integer AS services_today
        FROM kpi_service_history
        WHERE login_id = p_login_id 
            AND date_created::date = today_date
    )
    SELECT 
        h.hour_value::integer AS "HourValue",
        h.patrons_per_hour_count::integer AS "PatronsPerHourCount",
        h.services_per_hour_count::integer AS "ServicesPerHourCount",
        d.patrons_today::integer AS "PatronsToday",
        d.services_today::integer AS "ServicesToday"
    FROM hourly_data h
    CROSS JOIN daily_totals d
    ORDER BY h.hour_value;
END;
$$;


--
-- Name: mob_kpi_libraries_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_libraries_get(p_owner_id uuid) RETURNS TABLE("LibraryID" uuid, "LibraryName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        id AS "LibraryID", 
        name AS "LibraryName" 
    FROM lib_libraries 
    WHERE owner_id = p_owner_id
    ORDER BY name;
END;
$$;


--
-- Name: mob_kpi_patron_add(uuid, text, text, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_patron_add(p_owner_id uuid, p_name text, p_abis_code text, p_comment text, INOUT p_new_id integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    p_new_id := -1;
    
    IF NOT EXISTS (
        SELECT 1 
        FROM rdr_readers 
        WHERE owner_id = p_owner_id 
          AND (abis_code = p_abis_code OR rfid_code = p_abis_code)
    ) THEN
        INSERT INTO rdr_readers (name, abis_code, comment, owner_id)
        VALUES (p_name, p_abis_code, p_comment, p_owner_id)
        RETURNING id INTO p_new_id;
    END IF;
END;
$$;


--
-- Name: mob_kpi_patron_comment_modify(integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_patron_comment_modify(p_patron_id integer, p_comment text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    UPDATE rdr_readers 
    SET comment = p_comment
    WHERE id = p_patron_id;
END;
$$;


--
-- Name: mob_kpi_patron_history_get(integer, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_patron_history_get(p_patron_id integer, p_date_start timestamp with time zone, p_date_end timestamp with time zone) RETURNS TABLE("PeriodValue" integer, "PeriodName" text, "VisitsByPeriod" integer, "ServicesByPeriod" integer, "VisitsCount" integer, "BooksChekoutCount" integer, "BooksCheckInCount" integer, "BooksProlongCount" integer, "AdditionalServicesCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    period_type text;
    days_diff integer;
    date_start_date date;
    date_end_date date;
BEGIN
    date_start_date := p_date_start::date;
    date_end_date := p_date_end::date;
    days_diff := date_end_date - date_start_date;
    
    -- Определяем тип периода
    IF days_diff <= 1 THEN
        period_type := 'HOUR';
    ELSIF days_diff <= 31 THEN
        period_type := 'DAY';
    ELSIF days_diff <= 365 THEN
        period_type := 'MONTH';
    ELSE
        period_type := 'YEAR';
    END IF;

    -- Общая статистика (одинакова для всех)
    RETURN QUERY
    WITH total_stats AS (
        SELECT
            COUNT(DISTINCT sh.date_created::date)::integer AS visits_count,
            SUM(CASE WHEN s.type_id = 1 THEN 1 ELSE 0 END)::integer AS books_checkout_count,
            SUM(CASE WHEN s.type_id = 2 THEN 1 ELSE 0 END)::integer AS books_check_in_count,
            SUM(CASE WHEN s.type_id = 4 THEN 1 ELSE 0 END)::integer AS books_prolong_count,
            SUM(CASE WHEN s.type_id IS NULL THEN 1 ELSE 0 END)::integer AS additional_services_count
        FROM kpi_service_history sh
        LEFT JOIN kpi_services s ON s.id = sh.service_id
        WHERE sh.reader_id = p_patron_id
            AND sh.date_created >= date_start_date
            AND sh.date_created < (date_end_date + INTERVAL '1 day')
    )
    SELECT 
        sub."PeriodValue",
        sub."PeriodName",
        sub."VisitsByPeriod",
        sub."ServicesByPeriod",
        t.visits_count,
        t.books_checkout_count,
        t.books_check_in_count,
        t.books_prolong_count,
        t.additional_services_count
    FROM total_stats t
    CROSS JOIN LATERAL (
        -- Генерация периодов в зависимости от типа
        SELECT * FROM (
            -- Часы
            SELECT 
                (EXTRACT(HOUR FROM gs.h) - EXTRACT(HOUR FROM date_start_date::timestamp))::integer AS "PeriodValue",
                TO_CHAR(gs.h, 'HH24:00') AS "PeriodName",
                COALESCE(ps.visits_by_period, 0)::integer AS "VisitsByPeriod",
                COALESCE(ps.services_by_period, 0)::integer AS "ServicesByPeriod"
            FROM generate_series(
                date_start_date::timestamp, 
                date_start_date::timestamp + INTERVAL '23 hours', 
                INTERVAL '1 hour'
            ) AS gs(h)
            LEFT JOIN (
                SELECT 
                    EXTRACT(HOUR FROM sh.date_created)::integer AS hour_val,
                    COUNT(DISTINCT sh.date_created::date)::integer AS visits_by_period,
                    COUNT(*)::integer AS services_by_period
                FROM kpi_service_history sh
                WHERE sh.reader_id = p_patron_id
                    AND sh.date_created >= date_start_date
                    AND sh.date_created < (date_start_date + INTERVAL '1 day')
                GROUP BY EXTRACT(HOUR FROM sh.date_created)
            ) ps ON ps.hour_val = EXTRACT(HOUR FROM gs.h)
            WHERE period_type = 'HOUR'

            UNION ALL

            -- Дни
            SELECT 
                (gs.d::date - date_start_date)::integer AS "PeriodValue",
                TO_CHAR(gs.d, 'DD.MM.YYYY') AS "PeriodName",
                COALESCE(ps.visits_by_period, 0)::integer AS "VisitsByPeriod",
                COALESCE(ps.services_by_period, 0)::integer AS "ServicesByPeriod"
            FROM generate_series(
                date_start_date, 
                date_end_date, 
                INTERVAL '1 day'
            ) AS gs(d)
            LEFT JOIN (
                SELECT 
                    sh.date_created::date AS day_val,
                    COUNT(DISTINCT sh.date_created::date)::integer AS visits_by_period,
                    COUNT(*)::integer AS services_by_period
                FROM kpi_service_history sh
                WHERE sh.reader_id = p_patron_id
                    AND sh.date_created >= date_start_date
                    AND sh.date_created <= date_end_date
                GROUP BY sh.date_created::date
            ) ps ON ps.day_val = gs.d::date
            WHERE period_type = 'DAY'

            UNION ALL

            -- Месяцы
            SELECT 
                (EXTRACT(YEAR FROM gs.m) - EXTRACT(YEAR FROM date_start_date))::integer * 12 +
                (EXTRACT(MONTH FROM gs.m) - EXTRACT(MONTH FROM date_start_date))::integer AS "PeriodValue",
                TO_CHAR(gs.m, 'Month YYYY') AS "PeriodName",
                COALESCE(ps.visits_by_period, 0)::integer AS "VisitsByPeriod",
                COALESCE(ps.services_by_period, 0)::integer AS "ServicesByPeriod"
            FROM generate_series(
                DATE_TRUNC('month', date_start_date::timestamp),
                DATE_TRUNC('month', date_end_date::timestamp),
                INTERVAL '1 month'
            ) AS gs(m)
            LEFT JOIN (
                SELECT 
                    DATE_TRUNC('month', sh.date_created)::date AS month_val,
                    COUNT(DISTINCT sh.date_created::date)::integer AS visits_by_period,
                    COUNT(*)::integer AS services_by_period
                FROM kpi_service_history sh
                WHERE sh.reader_id = p_patron_id
                    AND sh.date_created >= date_start_date
                    AND sh.date_created <= date_end_date
                GROUP BY DATE_TRUNC('month', sh.date_created)
            ) ps ON ps.month_val = DATE_TRUNC('month', gs.m)::date
            WHERE period_type = 'MONTH'

            UNION ALL

         -- Годы
			SELECT 
			    (gs.y - EXTRACT(YEAR FROM date_start_date)::integer)::integer AS "PeriodValue",
			    gs.y::text AS "PeriodName",
			    COALESCE(ps.visits_by_period, 0)::integer AS "VisitsByPeriod",
			    COALESCE(ps.services_by_period, 0)::integer AS "ServicesByPeriod"
			FROM generate_series(
			    EXTRACT(YEAR FROM date_start_date)::integer,
			    EXTRACT(YEAR FROM date_end_date)::integer,
			    1
			) AS gs(y)
			LEFT JOIN (
			    SELECT 
			        EXTRACT(YEAR FROM sh.date_created)::integer AS year_val,
			        COUNT(DISTINCT sh.date_created::date)::integer AS visits_by_period,
			        COUNT(*)::integer AS services_by_period
			    FROM kpi_service_history sh
			    WHERE sh.reader_id = p_patron_id
			        AND sh.date_created >= date_start_date
			        AND sh.date_created <= date_end_date
			    GROUP BY EXTRACT(YEAR FROM sh.date_created)::integer
			) ps ON ps.year_val = gs.y
			WHERE period_type = 'YEAR'
        ) combined
        ORDER BY "PeriodValue"
    ) sub;
END;
$$;


--
-- Name: mob_kpi_patron_info_get(uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_patron_info_get(p_owner_id uuid, p_abis_code text) RETURNS TABLE("PatronID" integer, "DateCreated" timestamp with time zone, "PatronName" text, "AbisCode" text, "Comment" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        r.id AS "PatronID",
        r.date_created AS "DateCreated",
        r.name AS "PatronName",
        r.abis_code AS "AbisCode",
        r.comment AS "Comment"
    FROM rdr_readers r
    WHERE r.owner_id = p_owner_id 
        AND (r.abis_code = p_abis_code OR r.rfid_code = p_abis_code)
        AND r.abis_code IS NOT NULL
        AND r.abis_code != '';
END;
$$;


--
-- Name: mob_kpi_service_apply(integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_service_apply(p_login_id integer, p_service_id integer, p_patron_id integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    INSERT INTO kpi_service_history (login_id, reader_id, service_id)
    VALUES (p_login_id, p_patron_id, p_service_id);
END;
$$;


--
-- Name: mob_kpi_services_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_services_get(p_library_id uuid) RETURNS TABLE("ServiceID" integer, "ServiceName" text, "ServiceTypeID" integer, "ServiceTypeName" text, "IsAuthRequired" boolean, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        s.id AS "ServiceID",
        s.name AS "ServiceName",
        st.id AS "ServiceTypeID", 
        st.name AS "ServiceTypeName",
        s.is_auth_required AS "IsAuthRequired",
        s.library_id AS "LibraryID"
    FROM kpi_services s
    LEFT JOIN kpi_services_types st ON st.id = s.type_id
    WHERE s.library_id = p_library_id 
        AND s.is_deleted = false
    ORDER BY s.name ASC, s.type_id DESC;
END;
$$;


--
-- Name: mob_kpi_services_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_services_types_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT id, name FROM kpi_services_types;
END;
$$;


--
-- Name: mob_kpi_visitors_get(bytea, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.mob_kpi_visitors_get(p_camera_ids bytea, p_last_record_id integer) RETURNS TABLE("PatronID" integer, "PatronName" text, "RecordID" integer, "DateCreated" timestamp with time zone, "CameraID" integer, "AbisCode" text, "Comment" text, "Photo" bytea, "IsAuthenticated" boolean, "IsCommented" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        vp.reader_id AS "PatronID",
        r.name AS "PatronName",
        vp.id AS "RecordID",
        vp.date_created AS "DateCreated",
        vp.camera_id AS "CameraID",
        r.abis_code AS "AbisCode",
        r.comment AS "Comment",
        vp.photo AS "Photo",
        (vp.reader_id IS NOT NULL)::boolean AS "IsAuthenticated",
        (r.comment IS NOT NULL)::boolean AS "IsCommented"
    FROM fd_visitors_photo vp
    LEFT JOIN rdr_readers r ON r.id = vp.reader_id
    WHERE vp.camera_id = ANY(
        CASE 
            WHEN p_camera_ids IS NULL OR LENGTH(p_camera_ids) = 0 THEN ARRAY[]::integer[]
            ELSE (
                SELECT ARRAY(
                    SELECT 
                        (
                            (GET_BYTE(p_camera_ids, n * 4 + 0)::bigint << 24) +
                            (GET_BYTE(p_camera_ids, n * 4 + 1)::bigint << 16) +
                            (GET_BYTE(p_camera_ids, n * 4 + 2)::bigint << 8) +
                            (GET_BYTE(p_camera_ids, n * 4 + 3)::bigint)
                        )::integer
                    FROM generate_series(0, (LENGTH(p_camera_ids) / 4) - 1) AS n
                )
            ) 
        END
    )
        AND vp.id > COALESCE(p_last_record_id, 0)
        AND vp.date_created::date = CURRENT_DATE
    ORDER BY vp.date_created DESC;
END;
$$;


--
-- Name: monitor_device_add(uuid, uuid, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_device_add(p_libraryid uuid, p_deviceid uuid, p_name text, p_typeid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM monitor_devices WHERE device_id = p_deviceid AND library_id = p_libraryid) THEN
		INSERT INTO monitor_devices(device_id, name, type_id, library_id) VALUES(p_deviceid, p_name, p_typeid, p_libraryid);
	ELSE 
		UPDATE monitor_devices SET type_id = p_typeid, name = p_name
		WHERE device_id = p_deviceid AND library_id = p_libraryid;
	END IF;
END;
$$;


--
-- Name: monitor_device_data_add(uuid, integer, uuid, timestamp with time zone, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_device_data_add(p_internalguid uuid, p_id integer, p_deviceid uuid, p_datevalue timestamp with time zone, p_hourvalue integer, p_softonlinecount integer, p_deviceokcount integer, p_deviceerrorcount integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO monitor_device_data(id, internal_guid, device_id, date_value, hour_value, soft_online_count, device_ok_count, device_error_count) 
	VALUES(p_id, p_internalguid, p_deviceid, p_datevalue, p_hourvalue, p_softonlinecount, p_deviceokcount, p_deviceerrorcount);
END;
$$;


--
-- Name: monitor_device_data_clear(uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_device_data_clear(p_internalguid uuid, p_startid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	DELETE FROM monitor_device_data WHERE internal_guid = p_internalguid AND id >= p_startid;
END;
$$;


--
-- Name: monitor_gate_add(uuid, uuid, text, integer, text, integer, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_gate_add(p_libraryid uuid, p_id uuid, p_name text, p_typeid integer, p_ip text, p_port integer, p_lastupdate timestamp with time zone) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM monitor_gates WHERE id = p_id AND library_id = p_libraryid) THEN
		INSERT INTO monitor_gates(id, name, type_id, ip, port, library_id, last_update) VALUES(p_id, p_name, p_typeid, p_ip, p_port, p_libraryid, p_lastupdate);
	ELSE 
		UPDATE monitor_gates SET type_id = p_typeid, name = p_name, ip = p_ip, port = p_port, last_update = p_lastupdate
		WHERE id = p_id AND library_id = p_libraryid;
	END IF;
END;
$$;


--
-- Name: monitor_gate_alarms_add(uuid, bigint, uuid, timestamp with time zone, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_gate_alarms_add(p_internalguid uuid, p_id bigint, p_gateid uuid, p_datevalue timestamp with time zone, p_uid text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO monitor_gates_eas(id, internal_guid, gate_id, date_value, uid) 
	VALUES(p_id, p_internalguid, p_gateid, p_datevalue, p_uid);
END;
$$;


--
-- Name: monitor_gate_alarms_clear(uuid, bigint); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_gate_alarms_clear(p_internalguid uuid, p_startid bigint) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	DELETE FROM monitor_gates_eas WHERE internal_guid = p_internalguid AND id >= p_startid;
END;
$$;


--
-- Name: monitor_gate_counters_add(uuid, bigint, uuid, timestamp with time zone, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_gate_counters_add(p_internalguid uuid, p_id bigint, p_gateid uuid, p_datevalue timestamp with time zone, p_valuein integer, p_valueout integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO monitor_gates_counters(id, internal_guid, gate_id, date_value, value_in, value_out) 
	VALUES(p_id, p_internalguid, p_gateid, p_datevalue, p_valuein, p_valueout);
END;
$$;


--
-- Name: monitor_gate_counters_clear(uuid, bigint); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_gate_counters_clear(p_internalguid uuid, p_startid bigint) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	DELETE FROM monitor_gates_counters WHERE internal_guid = p_internalguid AND id >= p_startid;
END;
$$;


--
-- Name: monitor_gate_event_add(uuid, bigint, uuid, timestamp with time zone, integer, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_gate_event_add(p_internalguid uuid, p_id bigint, p_gateid uuid, p_datevalue timestamp with time zone, p_eventid integer, p_comment text, p_count integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO monitor_gates_event_history(id, internal_guid, gate_id, date_value, event_id, comment, count) 
	VALUES(p_id, p_internalguid, p_gateid, p_datevalue, p_eventid, p_comment, p_count);
END;
$$;


--
-- Name: monitor_gate_events_clear(uuid, bigint); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_gate_events_clear(p_internalguid uuid, p_startid bigint) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	DELETE FROM monitor_gates_event_history WHERE internal_guid = p_internalguid AND id >= p_startid;
END;
$$;


--
-- Name: monitor_internal_add_by_snap_shot(uuid, integer, timestamp with time zone, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_internal_add_by_snap_shot(p_guid_snapshot uuid, p_firmid integer, p_paymentdate timestamp with time zone, p_customname text, p_comment text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    INSERT INTO monitor_internals (guid, comment, payment_date, ip, firm_id, custom_name)
    SELECT s.guid, p_comment, p_paymentdate, s.ip, p_firmid, p_customname
    FROM monitor_internals_snapshot s
    WHERE s.guid = p_guid_snapshot 
      AND s.guid NOT IN (SELECT guid FROM monitor_internals);
END;
$$;


--
-- Name: monitor_internals_snapshot_add(uuid, text, timestamp with time zone, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_internals_snapshot_add(p_guid uuid, p_comment text, p_paymentdate timestamp with time zone, p_paymentdemomode integer, p_ip text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM monitor_internals_snapshot WHERE guid = p_guid) THEN
	INSERT INTO monitor_internals_snapshot(guid, comment, payment_date, payment_demo_mode, ip)
	VALUES(p_guid, p_comment, p_paymentdate, p_paymentdemomode, p_ip);
ELSE
	UPDATE monitor_internals_snapshot SET
		comment = p_comment,
		payment_date = p_paymentdate,
		payment_demo_mode = p_paymentdemomode,
		date_created = CURRENT_TIMESTAMP,
		ip = p_ip
	WHERE guid = p_guid;
END IF;
END;
$$;


--
-- Name: monitor_library_add(uuid, uuid, uuid, text, text, text, text, text, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_library_add(p_internalguid uuid, p_libraryid uuid, p_ownerid uuid, p_name text, p_fullname text, p_manager text, p_phone text, p_address text, p_tagservicecount integer, p_tagservicecounthash text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM monitor_libraries_snapshot WHERE internal_guid = p_internalguid AND guid = p_libraryid) THEN
		INSERT INTO monitor_libraries_snapshot(internal_guid, guid, name, full_name, manager, phone, address, tag_service_count, tag_service_count_hash) 
		VALUES(p_internalguid, p_libraryid, p_name, p_fullname, p_manager, p_phone, p_address, p_tagservicecount, p_tagservicecounthash);
	ELSE 
		UPDATE monitor_libraries_snapshot SET 
			name = p_name,
			full_name = p_fullname,
			manager = p_manager,
			phone = p_phone,
			address = p_address,
			tag_service_count = p_tagservicecount,
			tag_service_count_hash = p_tagservicecounthash,
			date_created = CURRENT_TIMESTAMP
		WHERE internal_guid = p_internalguid AND guid = p_libraryid;
	END IF;
END;
$$;


--
-- Name: monitor_owner_add(uuid, uuid, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_owner_add(p_internalguid uuid, p_id uuid, p_nameoriginal text, p_version integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM monitor_owners_snapshot WHERE id = p_id AND internal_guid = p_internalguid) THEN
		INSERT INTO monitor_owners_snapshot(id, internal_guid, name, version) 
			VALUES(p_id, p_internalguid, p_nameoriginal, p_version);
	ELSE
		UPDATE monitor_owners_snapshot SET
			name = p_nameoriginal,
			version = p_version,
			date_created = CURRENT_TIMESTAMP
		WHERE id = p_id AND internal_guid = p_internalguid;
	END IF;
END;
$$;


--
-- Name: monitor_service_snapshot_add(uuid, integer, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_service_snapshot_add(p_internalguid uuid, p_id integer, p_typeid integer, p_version text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM monitor_services_snapshot WHERE id_local = p_id AND internal_guid = p_internalguid) THEN
		INSERT INTO monitor_services_snapshot(id_local, internal_guid, type_id, version) 
		VALUES(p_id, p_internalguid, p_typeid, p_version);
	ELSE 
		UPDATE monitor_services_snapshot SET
			type_id = p_typeid,
			version = p_version
		WHERE id_local = p_id AND internal_guid = p_internalguid;
	END IF;
END;
$$;


--
-- Name: monitor_station_add(uuid, uuid, text, integer, text, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_station_add(p_libraryid uuid, p_id uuid, p_name text, p_stationtypeid integer, p_sn text, p_datecreated timestamp with time zone) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM monitor_stations WHERE id = p_id AND library_id = p_libraryid) THEN
		INSERT INTO monitor_stations(id, name, station_type_id, sn, library_id, date_created) VALUES(p_id, p_name, p_stationtypeid, p_sn, p_libraryid, p_datecreated);
	ELSE 
		UPDATE monitor_stations SET station_type_id = p_stationtypeid, name = p_name, sn = p_sn, last_update = CURRENT_TIMESTAMP
		WHERE id = p_id AND library_id = p_libraryid;
	END IF;
END;
$$;


--
-- Name: monitor_station_event_add(uuid, bigint, uuid, timestamp with time zone, integer, text, integer, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_station_event_add(p_internalguid uuid, p_id bigint, p_stationid uuid, p_dateevent timestamp with time zone, p_eventid integer, p_message text, p_count integer, p_usercode text, p_pairid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO monitor_stations_event_history(id, internal_guid, station_id, date_event, event_id, message, count, user_code, pair_id) 
	VALUES(p_id, p_internalguid, p_stationid, p_dateevent, p_eventid, p_message, p_count, p_usercode, p_pairid);
END;
$$;


--
-- Name: monitor_station_events_clear(uuid, bigint); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.monitor_station_events_clear(p_internalguid uuid, p_startid bigint) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	DELETE FROM monitor_stations_event_history WHERE internal_guid = p_internalguid AND id >= p_startid;
END;
$$;


--
-- Name: prop_ais_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_ais_get(p_loginid integer, p_ownerid uuid) RETURNS TABLE("ServerAddress" text, "ServerPort" integer, "ServerLogin" text, "ServerPassword" text, "DaysToReturnCount" integer, "BooksDB" text, "LocationCode" text, "InstitutionID" text, "IsAgeControlEnabled" boolean, "IsOnlyOneProlong" boolean, "IsCheckDebts" boolean, "IsAllowTakeIfDebts" boolean, "IsAllowReturnIfDebts" boolean, "IsAuthRequired" boolean, "IsUseFileLog" boolean, "IsUseSIP2" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		p.server_address::text AS "ServerAddress",
		p.server_port::integer AS "ServerPort",
		p.server_login::text AS "ServerLogin",
		p.server_password::text AS "ServerPassword",
		p.days_to_return_count::integer AS "DaysToReturnCount",
		p.books_db::text AS "BooksDB",
		p.location_code::text AS "LocationCode",
		p.institution_id::text AS "InstitutionID",
		p.is_age_control_enabled::boolean AS "IsAgeControlEnabled",
		p.is_only_one_prolong::boolean AS "IsOnlyOneProlong",
		p.is_check_debts::boolean AS "IsCheckDebts",
		p.is_allow_take_if_debts::boolean AS "IsAllowTakeIfDebts",
		p.is_allow_return_if_debts::boolean AS "IsAllowReturnIfDebts",
		p.is_auth_required::boolean AS "IsAuthRequired",
		p.is_use_file_log::boolean AS "IsUseFileLog",
		p.is_use_sip2::boolean AS "IsUseSIP2"
	FROM prop_ais p
	INNER JOIN auth_logins l ON l.id = p.login_id
	WHERE p.login_id = p_loginid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: prop_ais_modify(integer, uuid, text, integer, text, text, integer, text, text, text, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_ais_modify(p_loginid integer, p_ownerid uuid, p_serveraddress text, p_serverport integer, p_serverlogin text, p_serverpassword text, p_daystoreturncount integer, p_booksdb text, p_locationcode text, p_institutionid text, p_isagecontrolenabled boolean, p_isonlyoneprolong boolean, p_ischeckdebts boolean, p_isallowtakeifdebts boolean, p_isallowreturnifdebts boolean, p_isauthrequired boolean, p_isusefilelog boolean, p_isusesip2 boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM prop_ais WHERE login_id = p_loginid) THEN
		INSERT INTO prop_ais (login_id
							,server_address
							,server_port
							,server_login
							,server_password
							,days_to_return_count
							,books_db
							,location_code
							,institution_id
							,is_age_control_enabled
							,is_only_one_prolong
							,is_check_debts
							,is_allow_take_if_debts
							,is_allow_return_if_debts
							,is_auth_required
							,is_use_file_log
							,is_use_sip2)

						VALUES (p_loginid
							,p_serveraddress
							,p_serverport
							,p_serverlogin
							,p_serverpassword
							,p_daystoreturncount
							,p_booksdb
							,p_locationcode
							,p_institutionid
							,p_isagecontrolenabled
							,p_isonlyoneprolong
							,p_ischeckdebts
							,p_isallowtakeifdebts
							,p_isallowreturnifdebts
							,p_isauthrequired
							,p_isusefilelog
							,p_isusesip2);
	ELSE
		UPDATE prop_ais SET
							server_address = p_serveraddress
							,server_port = p_serverport
							,server_login = p_serverlogin
							,server_password = p_serverpassword
							,days_to_return_count = p_daystoreturncount
							,books_db = p_booksdb
							,location_code = p_locationcode
							,institution_id = p_institutionid
							,is_age_control_enabled = p_isagecontrolenabled
							,is_only_one_prolong = p_isonlyoneprolong
							,is_check_debts = p_ischeckdebts
							,is_allow_take_if_debts = p_isallowtakeifdebts
							,is_allow_return_if_debts = p_isallowreturnifdebts
							,is_auth_required = p_isauthrequired
							,is_use_file_log = p_isusefilelog
							,is_use_sip2 = p_isusesip2
		WHERE login_id = p_loginid;
	END IF;
END;
$$;


--
-- Name: prop_common_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_common_get(p_loginid integer, p_ownerid uuid) RETURNS TABLE("IsUseSendChar" boolean, "IsUseEAS_Alarms" boolean, "IsIgnoreDM_CardType" boolean, "ReceiptWidth" double precision, "IsUseEAS" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		p.is_use_send_char AS "IsUseSendChar",
		p.is_use_eas_alarms AS "IsUseEAS_Alarms",
		p.is_ignore_dm_card_type AS "IsIgnoreDM_CardType",
		p.receipt_width::float8 AS "ReceiptWidth",  
		p.is_use_eas AS "IsUseEAS"
	FROM prop_common p
	INNER JOIN auth_logins l ON l.id = p.login_id
	WHERE p.login_id = p_loginid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: prop_common_modify(integer, uuid, boolean, boolean, boolean, double precision, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_common_modify(p_loginid integer, p_ownerid uuid, p_isusesendchar boolean, p_isuseeas_alarms boolean, p_isignoredm_cardtype boolean, p_receiptwidth double precision, p_isuseeas boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM prop_common WHERE login_id = p_loginid) THEN
		INSERT INTO prop_common (login_id
							,is_use_send_char
							,is_use_eas_alarms
							,is_ignore_dm_card_type
							,receipt_width
							,is_use_eas)

						VALUES (p_loginid
							,p_isusesendchar
							,p_isuseeas_alarms
							,p_isignoredm_cardtype
							,p_receiptwidth
							,p_isuseeas);
	ELSE
		UPDATE prop_common SET
							is_use_send_char = p_isusesendchar
							,is_use_eas_alarms = p_isuseeas_alarms
							,is_ignore_dm_card_type = p_isignoredm_cardtype
							,receipt_width = p_receiptwidth
							,is_use_eas = p_isuseeas
		WHERE login_id = p_loginid;
	END IF;
END;
$$;


--
-- Name: prop_eas_modify(boolean, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_eas_modify(p_isuseeas boolean, p_loginid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	UPDATE prop_common
	SET is_use_eas = p_isuseeas
	WHERE login_id = p_loginid;
END;
$$;


--
-- Name: prop_email_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_email_get(p_loginid integer, p_ownerid uuid) RETURNS TABLE("IsUseEmailSender" boolean, "SMTP_ServerAddress" text, "SMTP_ServerPort" integer, "SenderAddress" text, "IsUseSSL" boolean, "SenderLogin" text, "SenderPassword" text, "EmailSubject" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		p.is_use_email_sender AS "IsUseEmailSender",
		p.smtp_server_address::text AS "SMTP_ServerAddress",  -- varchar(50) -> text
		p.smtp_server_port AS "SMTP_ServerPort",
		p.sender_address::text AS "SenderAddress",           -- varchar(50) -> text
		p.is_use_ssl AS "IsUseSSL",
		p.sender_login::text AS "SenderLogin",               -- varchar(50) -> text
		p.sender_password::text AS "SenderPassword",         -- varchar(50) -> text
		p.email_subject::text AS "EmailSubject"              -- varchar(250) -> text
	FROM prop_email p
	INNER JOIN auth_logins l ON l.id = p.login_id
	WHERE p.login_id = p_loginid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: prop_email_modify(integer, uuid, boolean, text, integer, text, boolean, text, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_email_modify(p_loginid integer, p_ownerid uuid, p_isuseemailsender boolean, p_smtpserveraddress text, p_smtpserverport integer, p_senderaddress text, p_isusessl boolean, p_senderlogin text, p_senderpassword text, p_emailsubject text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM prop_email WHERE login_id = p_loginid) THEN
		INSERT INTO prop_email (login_id
							,is_use_email_sender
							,smtp_server_address
							,smtp_server_port
							,sender_address
							,is_use_ssl
							,sender_login
							,sender_password
							,email_subject)

						VALUES (p_loginid
							,p_isuseemailsender
							,p_smtpserveraddress
							,p_smtpserverport
							,p_senderaddress
							,p_isusessl
							,p_senderlogin
							,p_senderpassword
							,p_emailsubject);
	ELSE
		UPDATE prop_email SET
							is_use_email_sender = p_isuseemailsender
							,smtp_server_address = p_smtpserveraddress
							,smtp_server_port = p_smtpserverport
							,sender_address = p_senderaddress
							,is_use_ssl = p_isusessl
							,sender_login = p_senderlogin
							,sender_password = p_senderpassword
							,email_subject = p_emailsubject
		WHERE login_id = p_loginid;
	END IF;
END;
$$;


--
-- Name: prop_face_detect_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_face_detect_get(p_loginid integer, p_ownerid uuid) RETURNS TABLE("CameraID" integer, "CameraAddress" text, "RegistratorAddress" text, "RegistratorPort" integer, "CameraChannel" integer, "RegistratorLogin" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		p.camera_id::integer AS "CameraID",                    -- int4 -> integer
		p.camera_address::text AS "CameraAddress",            -- varchar(50) -> text
		p.registrator_address::text AS "RegistratorAddress",  -- varchar(50) -> text
		p.registrator_port::integer AS "RegistratorPort",     -- int4 -> integer
		p.camera_channel::integer AS "CameraChannel",         -- int4 -> integer
		p.registrator_login::text AS "RegistratorLogin"       -- varchar(50) -> text
	FROM prop_face_detect p  -- Исправлено имя таблицы
	INNER JOIN auth_logins l ON l.id = p.login_id
	WHERE p.login_id = p_loginid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: prop_face_detect_modify(integer, uuid, integer, text, text, integer, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_face_detect_modify(p_loginid integer, p_ownerid uuid, p_cameraid integer, p_cameraaddress text, p_registratoraddress text, p_registratorport integer, p_camerachannel integer, p_registratorlogin text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM prop_face_detect WHERE login_id = p_loginid) THEN
		INSERT INTO prop_face_detect (login_id
							,camera_id
							,camera_address
							,registrator_address
							,registrator_port
							,camera_channel
							,registrator_login)

						VALUES (p_loginid
							,p_cameraid
							,p_cameraaddress
							,p_registratoraddress
							,p_registratorport
							,p_camerachannel
							,p_registratorlogin);
	ELSE
		UPDATE prop_face_detect SET
							camera_id = p_cameraid
							,camera_address = p_cameraaddress
							,registrator_address = p_registratoraddress
							,registrator_port = p_registratorport
							,camera_channel = p_camerachannel
							,registrator_login = p_registratorlogin
		WHERE login_id = p_loginid;
	END IF;
END;
$$;


--
-- Name: prop_proxy_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_proxy_get(p_loginid integer, p_ownerid uuid) RETURNS TABLE("IsUseProxy" boolean, "ProxyAddress" text, "ProxyPort" integer, "IsAuthRequired" boolean, "ProxyLogin" text, "ProxyPassword" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		COALESCE(p.is_use_proxy, false)::boolean AS "IsUseProxy",      -- NULL -> false
		p.proxy_address::text AS "ProxyAddress",                      -- varchar(50) -> text
		p.proxy_port::integer AS "ProxyPort",                         -- int4 -> integer
		COALESCE(p.is_auth_required, false)::boolean AS "IsAuthRequired", -- NULL -> false
		p.proxy_login::text AS "ProxyLogin",                          -- varchar(50) -> text
		p.proxy_password::text AS "ProxyPassword"                     -- varchar(50) -> text
	FROM prop_proxy_server p
	INNER JOIN auth_logins l ON l.id = p.login_id
	WHERE p.login_id = p_loginid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: prop_proxy_modify(integer, uuid, boolean, text, integer, boolean, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.prop_proxy_modify(p_loginid integer, p_ownerid uuid, p_isuseproxy boolean, p_proxyaddress text, p_proxyport integer, p_isauthrequired boolean, p_proxylogin text, p_proxypassword text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF NOT EXISTS (SELECT 1 FROM prop_proxy_server WHERE login_id = p_loginid) THEN
		INSERT INTO prop_proxy_server (login_id
							,is_use_proxy
							,proxy_address
							,proxy_port
							,is_auth_required
							,proxy_login
							,proxy_password)

						VALUES (p_loginid
							,p_isuseproxy
							,p_proxyaddress
							,p_proxyport
							,p_isauthrequired
							,p_proxylogin
							,p_proxypassword);
	ELSE
		UPDATE prop_proxy_server SET
							is_use_proxy = p_isuseproxy
							,proxy_address = p_proxyaddress
							,proxy_port = p_proxyport
							,is_auth_required = p_isauthrequired
							,proxy_login = p_proxylogin
							,proxy_password = p_proxypassword
		WHERE login_id = p_loginid;
	END IF;
END;
$$;


--
-- Name: stat_device_data_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_device_data_get(p_lastid integer, p_ownerid uuid) RETURNS TABLE("ID" integer, "DateValue" timestamp with time zone, "DeviceID" uuid, "HourValue" integer, "SoftOnlineCount" integer, "DeviceOKCount" integer, "DeviceErrorCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		dd.id AS "ID",
		dd.date_value AS "DateValue",
		dd.device_id AS "DeviceID",
		dd.hour_value AS "HourValue",
		dd.soft_online_count AS "SoftOnlineCount",
		dd.device_ok_count AS "DeviceOKCount",
		dd.device_error_count AS "DeviceErrorCount"
	FROM mon_device_data dd
	INNER JOIN mon_devices d ON d.id = dd.device_id
	INNER JOIN lib_libraries l ON l.id = d.library_id
	WHERE dd.id > p_lastid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: stat_devices_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_devices_get(p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "TypeID" integer, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
  RETURN QUERY
  SELECT
	d.id AS "ID",
	d.name AS "Name",
	dt.id AS "TypeID",
	d.library_id AS "LibraryID"
  FROM mon_devices d
  INNER JOIN mon_device_types dt ON dt.id = d.type_id
  INNER JOIN lib_libraries l ON l.id = d.library_id
  WHERE l.owner_id = p_ownerid;
END;
$$;


--
-- Name: stat_gates_counters_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_gates_counters_get(p_lastid integer, p_ownerid uuid) RETURNS TABLE("ID" integer, "DateValue" timestamp with time zone, "ValueIn" integer, "ValueOut" integer, "GateID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		c.id AS "ID",
		c.date_value AS "DateValue",
		c.value_in AS "ValueIn",
		c.value_out AS "ValueOut",
		c.gate_id AS "GateID"
	FROM mon_gates_counters c
	INNER JOIN mon_gates g ON g.id = c.gate_id
	INNER JOIN lib_libraries l ON l.id = g.library_id
	WHERE c.id > p_lastid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: stat_gates_eas_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_gates_eas_get(p_lastid integer, p_ownerid uuid) RETURNS TABLE("ID" integer, "DateValue" timestamp with time zone, "UID" text, "GateID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		eas.id AS "ID",
		eas.date_value AS "DateValue",
		eas.uid AS "UID",
		eas.gate_id AS "GateID"
	FROM mon_gates_eas eas
	INNER JOIN mon_gates g ON g.id = eas.gate_id
	INNER JOIN lib_libraries l ON l.id = g.library_id
	WHERE eas.id > p_lastid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: stat_gates_event_history_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_gates_event_history_get(p_lastid integer, p_ownerid uuid) RETURNS TABLE("ID" integer, "DateValue" timestamp with time zone, "Count" integer, "Comment" text, "EventID" integer, "GateID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		eh.id AS "ID",
		eh.date_value AS "DateValue",
		eh.count AS "Count",
		eh.comment AS "Comment",
		eh.event_id AS "EventID",
		eh.gate_id AS "GateID"
	FROM mon_gates_event_history eh
	INNER JOIN mon_gates g ON g.id = eh.gate_id
	INNER JOIN lib_libraries l ON l.id = g.library_id
	WHERE eh.id > p_lastid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: stat_gates_info_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_gates_info_get(p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "IP" text, "Port" integer, "TypeID" integer, "LastUpdate" timestamp with time zone, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT 
	id AS "ID",
	name AS "Name",
	ip AS "IP",
	port AS "Port",
	cfg_type_id AS "TypeID",
	last_update AS "LastUpdate",
	library_id AS "LibraryID"
	FROM mon_gates
	WHERE library_id IN (SELECT id FROM lib_libraries WHERE owner_id = p_ownerid);
END;
$$;


--
-- Name: stat_internal_update(timestamp with time zone, integer, bigint, bigint, bigint, bigint, bigint); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_internal_update(p_paymentdate timestamp with time zone, p_paymentdemomode integer, p_lastdevicedataid bigint, p_lastgateeventsid bigint, p_lastgatealarmsid bigint, p_lastgatecountersid bigint, p_laststyationeventsid bigint) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	UPDATE internal SET
	last_device_data_id = CASE WHEN p_lastdevicedataid IS NULL THEN last_device_data_id ELSE p_lastdevicedataid END,
	last_gate_events_id = CASE WHEN p_lastgateeventsid IS NULL THEN last_gate_events_id ELSE p_lastgateeventsid END,	
	last_gate_alarms_id = CASE WHEN p_lastgatealarmsid IS NULL THEN last_gate_alarms_id ELSE p_lastgatealarmsid END,
	last_gate_counters_id = CASE WHEN p_lastgatecountersid IS NULL THEN last_gate_counters_id ELSE p_lastgatecountersid END,
	last_station_events_id = CASE WHEN p_laststyationeventsid IS NULL THEN last_station_events_id ELSE p_laststyationeventsid END,
	payment_date = p_paymentdate,
	payment_demo_mode = p_paymentdemomode,
	date_bp = CURRENT_TIMESTAMP;
END;
$$;


--
-- Name: stat_internals_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_internals_get() RETURNS TABLE("ID" uuid, "PaymentDate" timestamp with time zone, "PaymentDemoMode" integer, "LastDeviceDataID" integer, "LastGateEventsID" integer, "LastGateAlarmsID" integer, "LastGateCountersID" integer, "LastStationEventsID" integer, "DateBP" timestamp with time zone, "InternalDate" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		id AS "ID",
		payment_date AS "PaymentDate",
		payment_demo_mode AS "PaymentDemoMode",
		last_device_data_id::integer AS "LastDeviceDataID",
		last_gate_events_id::integer AS "LastGateEventsID",
		last_gate_alarms_id::integer AS "LastGateAlarmsID",
		last_gate_counters_id::integer AS "LastGateCountersID",
		last_station_events_id::integer AS "LastStationEventsID",
		date_bp AS "DateBP",
		CURRENT_TIMESTAMP AS "InternalDate"
	FROM internal;
END;
$$;


--
-- Name: stat_libraries_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_libraries_get(p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "FullName" text, "Manager" text, "Phone" text, "Address" text, "DateCreated" timestamp with time zone, "TagServiceCount" integer, "TagServiceCountHash" text, "TS" integer, "Comment" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT 
		id AS "ID",
		name AS "Name",
		fullname AS "FullName",
		manager AS "Manager",
		phone AS "Phone",
		address AS "Address",
		date_created AS "DateCreated",
		tag_service_count AS "TagServiceCount",
		tag_service_count_hash AS "TagServiceCountHash",
		ts AS "TS",
		comment AS "Comment"
	FROM lib_libraries;
END;
$$;


--
-- Name: stat_library_modify(uuid, text, text, text, text, text, integer, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_library_modify(p_guid uuid, p_name text, p_fullname text, p_manager text, p_phone text, p_address text, p_tagservicecount integer, p_tagservicecounthash text, p_ownerid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF EXISTS (SELECT 1 FROM lib_libraries WHERE id = p_guid) THEN
		UPDATE lib_libraries SET
			name = p_name,
			fullname = p_fullname,
			manager = p_manager,
			phone = p_phone,
			address = p_address,
			tag_service_count = p_tagservicecount,
			tag_service_count_hash = p_tagservicecounthash
		WHERE id = p_guid;
	ELSE
		INSERT INTO lib_libraries (id, name, fullname, manager, phone, address, tag_service_count, tag_service_count_hash, owner_id)
		VALUES(p_guid, p_name, p_fullname, p_manager, p_phone, p_address, p_tagservicecount, p_tagservicecounthash, p_ownerid);
	END IF;
END;
$$;


--
-- Name: stat_mono_gate_count_devices_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_mono_gate_count_devices_get(p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "IP" text, "Port" integer, "DateLastOnline" timestamp with time zone, "GateID" uuid, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		mgcd.id AS "ID",
		mgcd.name AS "Name",
		mgcd.ip AS "IP",
		mgcd.port AS "Port",
		mgcd.date_last_online AS "DateLastOnline",
		mgcd.gate_id AS "GateID",
		g.library_id AS "LibraryID"
	FROM mon_mono_gate_count_devices mgcd
	INNER JOIN mon_gates g ON g.id = mgcd.gate_id
	INNER JOIN lib_libraries l ON l.id = g.library_id
	WHERE l.owner_id = p_ownerid;
END;
$$;


--
-- Name: stat_owner_modify(uuid, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_owner_modify(p_guid uuid, p_name text, p_version integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    int_count INTEGER;
    internal_id UUID;
BEGIN
	IF EXISTS (SELECT 1 FROM owners WHERE id = p_guid) THEN
		UPDATE owners SET
			name = p_name,
			version = p_version
		WHERE id = p_guid;
	ELSE
		SELECT COUNT(id) INTO int_count FROM internal;
		IF (int_count = 1) THEN
			SELECT id INTO internal_id FROM internal LIMIT 1;
			INSERT INTO owners (id, name, version, internal_id)
			VALUES(p_guid, p_name, p_version, internal_id);
		END IF;
	END IF;
END;
$$;


--
-- Name: stat_owners_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_owners_get() RETURNS TABLE("Name" text, "ID" uuid, "Version" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT name AS "Name",
		id AS "ID",
		version AS "Version"
	FROM owners;
END;
$$;


--
-- Name: stat_service_online_set(uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_service_online_set(p_libraryid uuid, p_version text DEFAULT ''::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
IF EXISTS(SELECT 1 FROM mon_services WHERE type_id = 7 AND library_id = p_libraryid) THEN
     UPDATE mon_services 
     SET 
       date_last_online = CURRENT_TIMESTAMP , 
       version = p_version
     WHERE library_id = p_libraryid AND type_id = 7;
ELSE
     INSERT INTO mon_services(type_id, date_last_online, version, library_id) VALUES(7, CURRENT_TIMESTAMP, p_version, p_libraryid);
END IF;
END;
$$;


--
-- Name: stat_services_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_services_get(p_ownerid uuid) RETURNS TABLE("ID" integer, "TypeID" integer, "Version" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT 
		id AS "ID",
		type_id AS "TypeID",
		version AS "Version"
	FROM mon_services
	WHERE library_id IN (SELECT id FROM lib_libraries WHERE owner_id = p_ownerid);
END;
$$;


--
-- Name: stat_stations_event_history_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_stations_event_history_get(p_lastid integer, p_ownerid uuid) RETURNS TABLE("ID" integer, "DateEvent" timestamp with time zone, "Count" integer, "EventID" integer, "Message" text, "UserCode" text, "PairID" integer, "StationID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		eh.id AS "ID",
		eh.date_event AS "DateEvent",
		eh.count AS "Count",
		eh.event_id AS "EventID",
		eh.message AS "Message",
		eh.user_code AS "UserCode",
		eh.pair_id AS "PairID",
		eh.station_id AS "StationID"
	FROM mon_stations_event_history eh
	INNER JOIN mon_stations s ON s.id = eh.station_id
	INNER JOIN lib_libraries l ON l.id = s.library_id
	WHERE eh.id > p_lastid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: stat_stations_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.stat_stations_get(p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "DateCreated" timestamp with time zone, "SN" text, "TypeID" integer, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
  RETURN QUERY
  SELECT
	s.id AS "ID",
	s.name AS "Name",
	s.date_created AS "DateCreated",
	s.sn AS "SN",
	st.id AS "TypeID",
	s.library_id AS "LibraryID"
  FROM mon_stations s
  INNER JOIN mon_station_types st ON st.id = s.station_type_id
  INNER JOIN lib_libraries l ON l.id = s.library_id
  WHERE l.owner_id = p_ownerid AND s.station_type_id <> 3 AND s.station_type_id <> 4;
END;
$$;


--
-- Name: sys_login_add_new(uuid, text, text, integer, text, text, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.sys_login_add_new(p_ownerid uuid, p_login text, p_pass text, p_roletypeid integer, p_firstname text, p_secondname text, p_lastname text, INOUT newid integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    pass_salt TEXT;
    admin_pass_md5 TEXT;
BEGIN
    IF EXISTS (SELECT 1 FROM auth_logins WHERE LOWER(p_login) = LOWER(login) AND owner_id = p_ownerid) THEN
        RETURN;
    END IF;
    
    pass_salt := REPLACE(gen_random_uuid()::TEXT, '-', '');
    admin_pass_md5 := MD5(pass_salt || p_pass);
    
    IF p_firstname IS NULL OR p_firstname = '' THEN
        p_firstname := 'Администратор';
    END IF;
    
    IF p_secondname IS NULL OR p_secondname = '' THEN
        p_secondname := 'Администратор';
    END IF;
    
    IF p_lastname IS NULL OR p_lastname = '' THEN
        p_lastname := 'Администратор';
    END IF;

    INSERT INTO auth_logins (owner_id, login, pass_md5, pass_salt, _pass, expired, first_name, second_name, last_name) 
    VALUES (p_ownerid, p_login, admin_pass_md5, pass_salt, p_pass, CURRENT_TIMESTAMP + INTERVAL '10 years', p_firstname, p_secondname, p_lastname)
    RETURNING id INTO newid;

    -- add permission roles
    INSERT INTO auth_login_roles (login_id, role_type_id)
    VALUES (newid, p_roletypeid);
END;
$$;


--
-- Name: sys_mainfirst_process(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.sys_mainfirst_process() RETURNS TABLE("LoginID" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    internal_id UUID;
    owner_id UUID;
    payment_date TIMESTAMP;
    new_login_id INTEGER;
    firm_id_internal INTEGER;
    firm_id_owners INTEGER;
    firm_id_libraries INTEGER;
    owner_name TEXT;
    internal_name TEXT;
    library_name TEXT;
    lib_full_name TEXT;
    login TEXT;
    password TEXT;
BEGIN
    payment_date := '2022-07-01'::TIMESTAMP;
    firm_id_internal := 462918;
    firm_id_owners := 462918;
    firm_id_libraries := 462918;
    internal_name := 'Зеленоград';
    owner_name := 'Зеленоград';
    library_name := 'Зеленоград';
    lib_full_name := 'Зеленоград';
    login := 'as_zelenog';
    password := 'as_zelenog_45678';

    IF NOT EXISTS(SELECT 1 FROM internal WHERE firm_id = firm_id_internal) THEN
        INSERT INTO internal(id, payment_date, name_text, comment, firm_id)
        VALUES(gen_random_uuid(), payment_date, internal_name, '', firm_id_internal);
    END IF;

    SELECT id INTO internal_id FROM internal WHERE firm_id = firm_id_internal LIMIT 1;
    IF NOT EXISTS(SELECT 1 FROM owners WHERE firm_id = firm_id_owners) THEN
        INSERT INTO owners (id, internal_id, name, firm_id)
        VALUES(gen_random_uuid(), internal_id, owner_name, firm_id_owners);
    END IF;

    SELECT id INTO owner_id FROM owners WHERE firm_id = firm_id_owners LIMIT 1;
    IF NOT EXISTS(SELECT 1 FROM lib_libraries WHERE firm_id = firm_id_libraries) THEN
        INSERT INTO lib_libraries (id, owner_id, name, full_name, firm_id, comment)  
        VALUES(gen_random_uuid(), owner_id, library_name, lib_full_name, firm_id_libraries, '');
    END IF;

    PERFORM sys_login_add_new(owner_id, login, password, 1, 'Администратор', 'Администратор', 'Администратор', new_login_id);
    
    RETURN QUERY SELECT new_login_id AS "LoginID";
END;
$$;


--
-- Name: upd_version_add(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.upd_version_add(p_version text, INOUT newid integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM upd_versions WHERE version = p_version) THEN
        INSERT INTO upd_versions (version)
        VALUES (p_version)
        RETURNING id INTO newid;

        UPDATE upd_versions 
        SET is_current = FALSE
        WHERE id <> newid;
    END IF;
END;
$$;


--
-- Name: upd_version_description_add(integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.upd_version_description_add(p_versionid integer, p_description text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	INSERT INTO upd_version_descriptions (version_id, description)
	VALUES (p_versionid, p_description);
END;
$$;


--
-- Name: upd_versions_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.upd_versions_get() RETURNS TABLE("VersionID" integer, "Version" text, "DateCreated" timestamp with time zone, "VersionDescription" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT vd.version_id AS "VersionID",
		v.version AS "Version",
		v.date_created AS "DateCreated",
		vd.description AS "VersionDescription"
	FROM upd_version_descriptions vd
	INNER JOIN upd_versions v ON v.id = vd.version_id;
END;
$$;


--
-- Name: web_acs_advanced_events_get(integer, integer, uuid, timestamp with time zone, timestamp with time zone, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_advanced_events_get(p_level integer, p_levelid integer, p_ownerid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "EventDate" timestamp with time zone, "RFIDCode" text, "ClientName" text, "ReaderID" integer, "DirectionTypeID" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_start DATE;
    date_end DATE;
BEGIN
    date_start := p_datestart::DATE;
    date_end := p_dateend::DATE;

    RETURN QUERY
    SELECT
        eh.id AS "ID",
        eh.event_date AS "EventDate",
        eh.rfid_code AS "RFIDCode",
        eh.client_name AS "ClientName",
        eh.reader_id AS "ReaderID",
        z.direction_type_id AS "DirectionTypeID",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM acs_events_history eh
    INNER JOIN acs_readers r ON eh.reader_id = r.id
    INNER JOIN acs_entrances e ON e.id = r.entrance_id
    INNER JOIN acs_reader_antennas ra ON ra.reader_id = eh.reader_id AND ra.reader_port_number = eh.reader_port_number
    INNER JOIN acs_zones z ON ra.zone_id = z.id
    WHERE 
        (eh.event_date::DATE BETWEEN date_start AND date_end)
        AND (
            (p_level = 1 AND e.id = p_levelid)
            OR (p_level = 2 AND z.id = p_levelid)
            OR (p_level = 3 AND ra.id = p_levelid)
        )
        AND p_ownerid = (SELECT owner_id FROM lib_libraries WHERE id = e.library_id LIMIT 1)
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN eh.event_date END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN eh.event_date END DESC NULLS FIRST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN eh.rfid_code END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN eh.rfid_code END DESC NULLS FIRST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN eh.client_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN eh.client_name END DESC NULLS FIRST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_acs_antenna_modify(integer, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_antenna_modify(p_antennaid integer, p_antennaname text, p_readerportnumber integer, p_zoneid integer, p_readerid integer, INOUT newid integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_antennaid IS NULL THEN
        INSERT INTO acs_reader_antennas (name, reader_port_number, zone_id, reader_id)
        VALUES (p_antennaname, p_readerportnumber, p_zoneid, p_readerid)
        RETURNING id INTO newid;
    ELSE
        UPDATE acs_reader_antennas
        SET name = p_antennaname,
            reader_port_number = p_readerportnumber,
            zone_id = p_zoneid,
            reader_id = p_readerid
        WHERE id = p_antennaid;
        newid := 0;
    END IF;
END;
$$;


--
-- Name: web_acs_direction_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_direction_types_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT id AS "ID", name AS "Name" FROM acs_direction_types;
END;
$$;


--
-- Name: web_acs_entrance_modify(integer, text, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_entrance_modify(p_entranceid integer, p_entrancename text, p_libraryid uuid, INOUT newid integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_entranceid IS NULL THEN
        INSERT INTO acs_entrances (name, library_id)
        VALUES (p_entrancename, p_libraryid)
        RETURNING id INTO newid;
    ELSE
        UPDATE acs_entrances
        SET name = p_entrancename,
            library_id = p_libraryid
        WHERE id = p_entranceid;
        newid := p_entranceid;
    END IF;
END;
$$;


--
-- Name: web_acs_events_get(uuid, uuid, timestamp with time zone, timestamp with time zone, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_events_get(p_libraryid uuid, p_ownerid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "RFIDCode" text, "ClientName" text, "DateIn" timestamp with time zone, "DateOut" timestamp with time zone, "MinutesBetween" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_datestart DATE;
    v_dateend DATE;
BEGIN
    v_datestart := p_datestart::DATE;
    v_dateend := p_dateend::DATE;

    RETURN QUERY
    WITH d AS (
        SELECT
            h.id,
            h.rfid_code,
            h.client_name,
            h.event_date,
            z.direction_type_id,
            LEAD(z.direction_type_id) OVER (
                PARTITION BY h.rfid_code 
                ORDER BY h.event_date, h.id
            ) AS nextdirid,
            LEAD(h.event_date) OVER (
                PARTITION BY h.rfid_code 
                ORDER BY h.event_date, h.id
            ) AS nextdate
        FROM acs_events_history h
        INNER JOIN acs_reader_antennas ra 
            ON ra.reader_id = h.reader_id 
            AND ra.reader_port_number = h.reader_port_number
        INNER JOIN acs_zones z ON z.id = ra.zone_id
        INNER JOIN acs_entrances en ON en.id = z.entrance_id
        INNER JOIN lib_libraries l ON l.id = en.library_id
        WHERE (
            (p_libraryid IS NOT NULL AND en.library_id = p_libraryid) 
            OR (p_libraryid IS NULL AND l.owner_id = p_ownerid)
        )
        AND h.event_date::DATE BETWEEN v_datestart AND v_dateend
    ),
    results AS (
        SELECT 
            d.id AS "ID",
            d.rfid_code AS "RFIDCode",
            d.client_name AS "ClientName",
            d.event_date AS "DateIn",
            CASE WHEN d.nextdirid = 2 THEN d.nextdate END AS "DateOut",
            CASE WHEN d.nextdirid = 2 THEN 
                CAST(EXTRACT(EPOCH FROM (d.nextdate - d.event_date)) / 60 AS INTEGER)
            END AS "MinutesBetween",
            CASE 
                WHEN d.direction_type_id = 1 
                     AND d.nextdirid IS DISTINCT FROM 2 
                     AND d.event_date::DATE = CURRENT_DATE 
                THEN 0 
                ELSE 1 
            END AS sort_priority
        FROM d
        WHERE d.direction_type_id = 1
          AND (
                d.nextdirid = 2
                OR 
                (d.nextdirid IS DISTINCT FROM 2 AND d.event_date::DATE = CURRENT_DATE)
              )
    ),
    -- Считаем общее количество ДО пагинации
    final_with_count AS (
        SELECT 
            r."ID",
            r."RFIDCode",
            r."ClientName",
            r."DateIn",
            r."DateOut",
            r."MinutesBetween",
            r.sort_priority,
            COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
        FROM results r
    )
    SELECT 
        f."ID",
        f."RFIDCode",
        f."ClientName",
        f."DateIn",
        f."DateOut",
        f."MinutesBetween",
        f."RowsCount"
    FROM final_with_count f
    ORDER BY 
        f.sort_priority ASC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN f."ClientName" END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN f."ClientName" END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN f."RFIDCode" END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN f."RFIDCode" END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN f."DateIn" END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN f."DateIn" END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN f."DateOut" END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN f."DateOut" END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN f."MinutesBetween" END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN f."MinutesBetween" END DESC
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_acs_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" integer, "Name" text, "LibraryID" uuid, "ZoneID" integer, "ZoneName" text, "DirectionTypeID" integer, "DirectionTypeName" text, "ReaderID" integer, "ReaderTypeID" integer, "ReaderTypeName" text, "IPAddress" text, "IsOnline" integer, "Port" integer, "ReaderName" text, "AntennaID" integer, "AntennaName" text, "ReaderPortNumber" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        e.id AS "ID",
        e.name AS "Name",
        e.library_id AS "LibraryID",
        z.id AS "ZoneID",
        z.name AS "ZoneName",
        z.direction_type_id AS "DirectionTypeID",
        dt.name AS "DirectionTypeName",
        r.id AS "ReaderID",
        rt.id AS "ReaderTypeID",
        rt.name AS "ReaderTypeName",
        r.ip_address AS "IPAddress",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - r.last_update)) / 60, 3) < 2 THEN 1 ELSE 0 END AS "IsOnline",
        r.port AS "Port",
        r.name AS "ReaderName",
        a.id AS "AntennaID",
        a.name AS "AntennaName",
        a.reader_port_number AS "ReaderPortNumber"
    FROM acs_entrances e
    INNER JOIN lib_libraries l ON l.id = e.library_id
    -- Центр — антенна, она связывает зону и ридер
    INNER JOIN acs_reader_antennas a ON true
    INNER JOIN acs_zones z ON z.id = a.zone_id AND z.entrance_id = e.id
    INNER JOIN acs_readers r ON r.id = a.reader_id AND r.entrance_id = e.id
    INNER JOIN acs_direction_types dt ON dt.id = z.direction_type_id
    INNER JOIN acs_readers_types rt ON rt.id = r.reader_type_id
    WHERE (p_libraryid IS NOT NULL AND l.id = p_libraryid)
       OR (p_libraryid IS NULL AND l.owner_id = p_ownerid);
END;
$$;


--
-- Name: web_acs_reader_modify(integer, text, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_reader_modify(p_readerid integer, p_readername text, p_readeraddress text, p_readerport integer, p_readertypeid integer, p_entranceid integer, INOUT newid integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_readerid IS NULL THEN
        INSERT INTO acs_readers (name, ip_address, port, reader_type_id, entrance_id)
        VALUES (p_readername, p_readeraddress, p_readerport, p_readertypeid, p_entranceid)
        RETURNING id INTO newid;
    ELSE
        UPDATE acs_readers
        SET name = p_readername,
            ip_address = p_readeraddress,
            port = p_readerport,
            reader_type_id = p_readertypeid,
            entrance_id = p_entranceid
        WHERE id = p_readerid;
        newid := p_readerid;
    END IF;
END;
$$;


--
-- Name: web_acs_readers_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_readers_types_get() RETURNS TABLE("ID" integer, "Name" text, "PortsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT id AS "ID", name AS "Name", ports_count AS "PortsCount" FROM acs_readers_types;
END;
$$;


--
-- Name: web_acs_zone_modify(integer, text, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_acs_zone_modify(p_zoneid integer, p_zonename text, p_entranceid integer, p_directiontypeid integer, INOUT newid integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_zoneid IS NULL THEN
        INSERT INTO acs_zones (name, entrance_id, direction_type_id)
        VALUES (p_zonename, p_entranceid, p_directiontypeid)
        RETURNING id INTO newid;
    ELSE
        UPDATE acs_zones
        SET name = p_zonename,
            entrance_id = p_entranceid,
            direction_type_id = p_directiontypeid
        WHERE id = p_zoneid;
        newid := p_zoneid;
    END IF;
END;
$$;


--
-- Name: web_bk_background_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_background_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" integer, "Background" bytea, "Opacity" double precision, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        b.id AS "ID",
        b.background AS "Background",
        b.opacity::float8 AS "Opacity",  
        b.library_id AS "LibraryID"
    FROM bk_backgrounds b
    INNER JOIN lib_libraries l ON l.id = b.library_id
    WHERE l.id = p_libraryid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: web_bk_background_modify(integer, bytea, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_background_modify(p_backgroundid integer, p_background bytea, p_libraryid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_backgroundid IS NULL AND p_background IS NOT NULL THEN
		INSERT INTO bk_backgrounds (background, library_id)
		VALUES (p_background, p_libraryid);
	END IF;

	IF p_backgroundid IS NOT NULL AND p_background IS NOT NULL THEN
		UPDATE bk_backgrounds
		SET background = p_background
		WHERE id = p_backgroundid AND library_id = p_libraryid;
	END IF;

	IF p_backgroundid IS NOT NULL AND p_background IS NOT NULL AND LENGTH(p_background) = 0 THEN
		DELETE FROM bk_backgrounds
		WHERE id = p_backgroundid AND library_id = p_libraryid;
	END IF;
END;
$$;


--
-- Name: web_bk_background_opacity_modify(integer, double precision, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_background_opacity_modify(p_backgroundid integer, p_opacity double precision, p_libraryid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	UPDATE bk_backgrounds
	SET opacity = p_opacity
	WHERE id = p_backgroundid AND library_id = p_libraryid;
END;
$$;


--
-- Name: web_bk_book_case_modify(integer, integer, text, text, integer, uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_case_modify(p_opid integer, p_bookcaseid integer, p_name text, p_coordinates text, p_sectionid integer, p_libraryid uuid, p_ownerid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF EXISTS (SELECT 1 FROM lib_libraries WHERE id = p_libraryid AND owner_id = p_ownerid LIMIT 1) THEN
		IF p_opid = 1 THEN
			IF p_coordinates IS NULL THEN
				p_coordinates := '1;1;0;1';
			END IF;
			INSERT INTO bk_book_cases (name, book_case_section_id, coordinates, library_id)
			VALUES (p_name, p_sectionid, p_coordinates, p_libraryid);
		END IF;

		IF p_opid = 2 THEN
			UPDATE bk_book_cases
			SET name = p_name,
				book_case_section_id = p_sectionid,
				coordinates = CASE WHEN p_coordinates IS NULL THEN coordinates ELSE p_coordinates END,
				library_id = p_libraryid
			WHERE id = p_bookcaseid;
		END IF;

		IF p_opid = 0 THEN
			DELETE FROM bk_history
			WHERE book_cell_id IN 
				(SELECT id FROM bk_book_cells WHERE book_shelf_id IN
					(SELECT id FROM bk_book_shelves WHERE book_case_id = p_bookcaseid));

			DELETE FROM bk_book_cells
			WHERE id IN (SELECT id FROM bk_book_cells WHERE book_shelf_id IN 
					(SELECT id FROM bk_book_shelves WHERE book_case_id = p_bookcaseid));

			DELETE FROM bk_book_shelves
			WHERE id IN (SELECT id FROM bk_book_shelves WHERE book_case_id = p_bookcaseid);

			DELETE FROM bk_book_cases WHERE id = p_bookcaseid;
		END IF;
	END IF;
END;
$$;


--
-- Name: web_bk_book_case_section_modify(integer, text, uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_case_section_modify(p_booksectionid integer, p_booksectionname text, p_libraryid uuid, p_ownerid uuid, p_opid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_opid = 1 THEN
		INSERT INTO bk_book_case_sections (name, library_id)
		VALUES (p_booksectionname, p_libraryid);
	END IF;

	IF p_opid = 2 THEN
		UPDATE bk_book_case_sections
		SET name = p_booksectionname
		WHERE id = p_booksectionid;
	END IF;

	IF p_opid = 0 THEN
		DELETE FROM bk_history
		WHERE book_cell_id IN 
			(SELECT id FROM bk_book_cells WHERE book_shelf_id IN
				(SELECT id FROM bk_book_shelves WHERE book_case_id IN
					(SELECT id FROM bk_book_cases WHERE book_case_section_id = p_booksectionid)));

		DELETE FROM bk_book_cells WHERE book_shelf_id IN
			(SELECT id FROM bk_book_shelves WHERE book_case_id IN
					(SELECT id FROM bk_book_cases WHERE book_case_section_id = p_booksectionid));

		DELETE FROM bk_book_shelves WHERE book_case_id IN
					(SELECT id FROM bk_book_cases WHERE book_case_section_id = p_booksectionid);

		DELETE FROM bk_book_cases WHERE book_case_section_id = p_booksectionid;

		DELETE FROM bk_book_case_sections WHERE id = p_booksectionid;
	END IF;
END;
$$;


--
-- Name: web_bk_book_case_sections_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_case_sections_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" integer, "Name" text, "BookCasesCount" integer, "BooksInSectionCount" integer, "BooksReturnedCount" integer, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        bks.id AS "ID",
        bks.name AS "Name",
        (SELECT COUNT(id) FROM bk_book_cases WHERE book_case_section_id = bks.id)::integer AS "BookCasesCount",
        (
            SELECT COUNT(book_id) 
            FROM bk_books b
            INNER JOIN bk_book_cells bcl ON bcl.id = b.cell_id
            INNER JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
            INNER JOIN bk_book_cases bc ON bc.id = bs.book_case_id
            WHERE bc.book_case_section_id = bks.id
        )::integer AS "BooksInSectionCount",
        (
            SELECT COUNT(book_id) 
            FROM bk_books b
            INNER JOIN bk_book_cells bcl ON bcl.id = b.cell_id
            INNER JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
            INNER JOIN bk_book_cases bc ON bc.id = bs.book_case_id
            WHERE bc.book_case_section_id = bks.id AND b.state_id = 3
        )::integer AS "BooksReturnedCount",
        bks.library_id AS "LibraryID"
    FROM bk_book_case_sections bks
    INNER JOIN lib_libraries l ON l.id = bks.library_id
    WHERE 
        (p_libraryid IS NOT NULL AND l.id = p_libraryid) 
        OR 
        (p_libraryid IS NULL AND l.owner_id = p_ownerid)
    ORDER BY "BooksReturnedCount" DESC, bks.name;
END;
$$;


--
-- Name: web_bk_book_cases_and_shelves_get(integer, uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_cases_and_shelves_get(p_sectionid integer, p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("BookCaseSectionID" integer, "BookCaseSectionName" text, "BookCaseID" integer, "BookCaseName" text, "BooksOnCaseCount" integer, "BooksOnCaseReturnedCount" integer, "BookShelvesCount" integer, "BookShelfID" integer, "BookShelfName" text, "BooksOnShelfCount" integer, "BooksOnShelfReturnedCount" integer, "Coordinates" text, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_owner_from_library UUID;
BEGIN
    SELECT INTO v_owner_from_library owner_id FROM lib_libraries WHERE id = p_libraryid LIMIT 1;

    IF p_ownerid <> v_owner_from_library THEN
        RETURN;
    END IF;

    RETURN QUERY
    SELECT 
        bks.id AS "BookCaseSectionID",
        bks.name AS "BookCaseSectionName",
        bc.id AS "BookCaseID",
        bc.name AS "BookCaseName",
        COALESCE(boc.count_books, 0) AS "BooksOnCaseCount",
        COALESCE(bocr.count_returned_books, 0) AS "BooksOnCaseReturnedCount",
        (SELECT COUNT(id) FROM bk_book_shelves WHERE book_case_id = bc.id)::integer AS "BookShelvesCount",
        bs.id AS "BookShelfID",
        bs.name AS "BookShelfName",
        COALESCE(bos.count_books, 0) AS "BooksOnShelfCount",
        COALESCE(bosr.count_returned_books, 0) AS "BooksOnShelfReturnedCount",
        bc.coordinates AS "Coordinates",
        bc.library_id AS "LibraryID"
    FROM bk_book_cases bc
    INNER JOIN bk_book_case_sections bks ON bks.id = bc.book_case_section_id
    LEFT JOIN bk_book_shelves bs ON bc.id = bs.book_case_id
    LEFT JOIN (
        SELECT bs2.book_case_id, COUNT(b2.book_id)::integer AS count_books
        FROM bk_books b2
        INNER JOIN bk_book_cells bc2 ON bc2.id = b2.cell_id
        INNER JOIN bk_book_shelves bs2 ON bs2.id = bc2.book_shelf_id
        GROUP BY bs2.book_case_id
    ) boc ON boc.book_case_id = bc.id
    LEFT JOIN (
        SELECT bs2.book_case_id, COUNT(b2.book_id)::integer AS count_returned_books
        FROM bk_books b2
        INNER JOIN bk_book_cells bc2 ON bc2.id = b2.cell_id
        INNER JOIN bk_book_shelves bs2 ON bs2.id = bc2.book_shelf_id
        WHERE b2.state_id = 3
        GROUP BY bs2.book_case_id
    ) bocr ON bocr.book_case_id = bc.id
    LEFT JOIN (
        SELECT bc2.book_shelf_id, COUNT(b2.book_id)::integer AS count_books
        FROM bk_books b2
        INNER JOIN bk_book_cells bc2 ON bc2.id = b2.cell_id
        GROUP BY bc2.book_shelf_id
    ) bos ON bos.book_shelf_id = bs.id
    LEFT JOIN (
        SELECT bc2.book_shelf_id, COUNT(b2.book_id)::integer AS count_returned_books
        FROM bk_books b2
        INNER JOIN bk_book_cells bc2 ON bc2.id = b2.cell_id
        WHERE b2.state_id = 3
        GROUP BY bc2.book_shelf_id
    ) bosr ON bosr.book_shelf_id = bs.id
    WHERE bks.id = p_sectionid AND bc.is_deleted = false
    GROUP BY 
        bks.id, bks.name, bc.id, bc.name, boc.count_books, bocr.count_returned_books,
        bs.id, bs.name, bos.count_books, bosr.count_returned_books, bc.coordinates, bc.library_id
    ORDER BY bocr.count_returned_books DESC NULLS LAST, bosr.count_returned_books DESC NULLS LAST;
END;
$$;


--
-- Name: web_bk_book_cell_modify(integer, integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_cell_modify(p_opid integer, p_bookshelfid integer, p_bookcellid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_opid = 1 THEN
		INSERT INTO bk_book_cells (book_shelf_id, number)
		VALUES (p_bookshelfid, (COALESCE((SELECT MAX(number) FROM bk_book_cells WHERE book_shelf_id = p_bookshelfid), 0) + 1));
	END IF;

	IF p_opid = 0 THEN
		DELETE FROM bk_history WHERE book_cell_id = p_bookcellid;
		DELETE FROM bk_book_cells WHERE id = p_bookcellid;
	END IF;
END;
$$;


--
-- Name: web_bk_book_cells_on_shelf_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_cells_on_shelf_get(p_bookshelfid integer, p_ownerid uuid) RETURNS TABLE("ID" uuid, "Number" integer, "BookShelfID" integer, "BooksOnCellCount" integer, "BooksReturnedCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        bcl.id AS "ID",
        bcl.number AS "Number",
        bcl.book_shelf_id AS "BookShelfID",
        (SELECT COUNT(book_id) FROM bk_books WHERE cell_id = bcl.id)::integer AS "BooksOnCellCount",
        (SELECT COUNT(book_id) FROM bk_books WHERE cell_id = bcl.id AND state_id = 3)::integer AS "BooksReturnedCount"
    FROM bk_book_cells bcl
    WHERE bcl.book_shelf_id = p_bookshelfid
    ORDER BY bcl.number;
END;
$$;


--
-- Name: web_bk_book_info_by_epc_get(text, uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_info_by_epc_get(p_bookcode text, p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("BookID" integer, "BookCode" text, "BookName" text, "AuthorsName" text, "BkStateID" integer, "BkStateName" text, "BookCellID" uuid, "BookCellNumber" integer, "BookShelfID" integer, "BookShelfName" text, "BookCaseID" integer, "BookCaseName" text, "BookCaseSectionID" integer, "BookCaseSectionName" text, "PlaceFullName" text, "ExistsInLibBooks" boolean, "ExistsInBkBooks" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_ownerid != (SELECT owner_id FROM lib_libraries WHERE id = p_libraryid LIMIT 1) THEN
        RETURN;
    END IF;

    RETURN QUERY
    SELECT
        lb.id AS "BookID",
        lb.book_code AS "BookCode",
        lb.book_name AS "BookName",
        lb.authors_name AS "AuthorsName",
        bb.state_id AS "BkStateID",
        bst.name AS "BkStateName",
        bb.cell_id AS "BookCellID",
        bcl.number AS "BookCellNumber",
        bs.id AS "BookShelfID",
        bs.name AS "BookShelfName",
        bc.id AS "BookCaseID",
        bc.name AS "BookCaseName",
        bcs.id AS "BookCaseSectionID",
        bcs.name AS "BookCaseSectionName",
        CASE
            WHEN bcs.name IS NOT NULL THEN
                bcs.name || ' > ' || bc.name || ' > ' || bs.name || ' > ' || bcl.number::text
            ELSE NULL
        END AS "PlaceFullName",
        lb.id IS NOT NULL AS "ExistsInLibBooks",
        bb.book_id IS NOT NULL AS "ExistsInBkBooks"
    FROM lib_books lb
    LEFT JOIN bk_books bb ON bb.book_id = lb.id
    LEFT JOIN bk_book_states bst ON bst.id = bb.state_id
    LEFT JOIN bk_book_cells bcl ON bcl.id = bb.cell_id
    LEFT JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
    LEFT JOIN bk_book_cases bc ON bc.id = bs.book_case_id
    LEFT JOIN bk_book_case_sections bcs ON bcs.id = bc.book_case_section_id
    WHERE lb.book_code = p_bookcode
      AND lb.library_id IN (SELECT id FROM lib_libraries WHERE owner_id = p_ownerid);
END;
$$;


--
-- Name: web_bk_book_inventory_states_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_inventory_states_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT id AS "ID", name AS "Name" FROM bk_book_inv_states;
END;
$$;


--
-- Name: web_bk_book_remove(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_remove(p_bookid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    DELETE FROM bk_history WHERE book_id = p_bookid;
    DELETE FROM bk_books WHERE book_id = p_bookid;
END;
$$;


--
-- Name: web_bk_book_search(text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_search(p_searchstring text, p_libraryid uuid) RETURNS TABLE("BookID" integer, "BookCode" text, "BookName" text, "AuthorsName" text, "BookCellID" uuid, "BookCellNumber" integer, "BookShelfName" text, "BookCaseName" text, "PlaceFullName" text, "StateID" integer, "StateName" text)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_word text;
BEGIN
    v_word := '%' || p_searchString || '%';
    RETURN QUERY
    SELECT 
        b.book_id,
        lb.book_code,
        lb.book_name,
        lb.authors_name,
        bcl.id,
        bcl.number,
        bs.name,
        bc.name,
        bcs.name || ' > ' || bc.name || ' > ' || bs.name || ' > ' || CAST(bcl.number AS text),
        b.state_id,
        bst.name
    FROM bk_books b
    INNER JOIN lib_books lb ON lb.id = b.book_id
    INNER JOIN bk_book_cells bcl ON bcl.id = b.cell_id
    INNER JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
    INNER JOIN bk_book_cases bc ON bc.id = bs.book_case_id
    INNER JOIN bk_book_case_sections bcs ON bcs.id = bc.book_case_section_id
    INNER JOIN bk_book_states bst ON bst.id = b.state_id
    WHERE (bc.library_id = p_libraryid) AND (lb.book_code ILIKE v_word OR lb.book_name ILIKE v_word);
END;
$$;


--
-- Name: web_bk_book_shelf_modify(integer, integer, integer, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_shelf_modify(p_opid integer, p_bookcaseid integer, p_bookshelfid integer, p_name text, p_ownerid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
        v_id INTEGER;
BEGIN
        IF EXISTS (SELECT 1 FROM bk_book_cases WHERE id = p_bookcaseid AND library_id IN (SELECT id FROM lib_libraries WHERE owner_id = p_ownerid) LIMIT 1) THEN
                IF p_opid = 1 THEN
                        v_id := -1;

                        INSERT INTO bk_book_shelves (name, book_case_id)
                        VALUES (p_name, p_bookcaseid)
                        RETURNING id INTO v_id;

                        PERFORM web_bk_book_cell_modify(1, v_id, NULL::uuid);

                END IF;

                IF p_opid = 2 THEN
                        UPDATE bk_book_shelves
                        SET name = p_name,
                                book_case_id = p_bookcaseid
                        WHERE id = p_bookshelfid;
                END IF;

                IF p_opid = 0 THEN
                        DELETE FROM bk_history
                        WHERE book_cell_id IN 
                                (SELECT id FROM bk_book_cells WHERE book_shelf_id = p_bookshelfid);

                        DELETE FROM bk_book_cells
                        WHERE id IN (SELECT id FROM bk_book_cells WHERE book_shelf_id = p_bookshelfid);

                        DELETE FROM bk_book_shelves
                        WHERE id = p_bookshelfid;
                END IF;
        END IF;
END;
$$;


--
-- Name: web_bk_book_to_cell_bind(integer, uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_to_cell_bind(p_bookid integer, p_bookcellid uuid, p_ownerid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM bk_books WHERE book_id = p_bookid LIMIT 1) THEN
        INSERT INTO bk_books (book_id, cell_id, state_id)
        VALUES (p_bookid, p_bookcellid, 1);
    ELSE
        UPDATE bk_books
        SET cell_id = p_bookcellid,
            state_id = 1
        WHERE book_id = p_bookid;
    END IF;
END;
$$;


--
-- Name: web_bk_book_to_lib_books_add(text, text, text, integer, text, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_book_to_lib_books_add(p_bookcode text, p_bookname text DEFAULT NULL::text, p_authorsname text DEFAULT NULL::text, p_isbook integer DEFAULT 1, p_bookplace text DEFAULT NULL::text, p_libraryid uuid DEFAULT NULL::uuid, p_stateid integer DEFAULT NULL::integer) RETURNS TABLE("NewBookID" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    INSERT INTO lib_books (book_code, book_name, authors_name, is_book, book_place, library_id, state_id)
    VALUES (p_bookcode, p_bookname, p_authorsname, p_isbook, p_bookplace, p_libraryid, p_stateid)
    RETURNING id;
END;
$$;


--
-- Name: web_bk_books_on_cell_get(uuid, uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_books_on_cell_get(p_cellid uuid, p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("BookID" integer, "BookName" text, "BookCode" text, "StateID" integer, "StateName" text, "BookCellID" uuid, "BookCellNumber" integer, "BookShelfID" integer, "BookShelfName" text, "BookCaseID" integer, "BookCaseName" text, "FullPlaceName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        bb.book_id AS "BookID",  
        lb.book_name AS "BookName",
        lb.book_code AS "BookCode",
        bb.state_id AS "StateID",
        s.name AS "StateName",
        bb.cell_id AS "BookCellID",
        bcl.number AS "BookCellNumber",
        bs.id AS "BookShelfID",
        bs.name AS "BookShelfName",
        bc.id AS "BookCaseID",
        bc.name AS "BookCaseName",
        bcs.name || ' > ' || bc.name || ' > ' || bs.name || ' > ' || bcl.number::TEXT AS "FullPlaceName"
    FROM bk_books bb
    INNER JOIN bk_book_states s ON s.id = bb.state_id
    INNER JOIN lib_books lb ON lb.id = bb.book_id
    INNER JOIN bk_book_cells bcl ON bcl.id = bb.cell_id
    INNER JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
    INNER JOIN bk_book_cases bc ON bc.id = bs.book_case_id
    INNER JOIN bk_book_case_sections bcs ON bcs.id = bc.book_case_section_id
    WHERE (bb.cell_id = p_cellid) 
      AND (p_ownerid = (SELECT owner_id FROM lib_libraries WHERE id = p_libraryid LIMIT 1))
    ORDER BY lb.book_name;
END;
$$;


--
-- Name: web_bk_environment_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_environment_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" integer, "ColorID" integer, "Color_HTML" text, "Coordinates" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT e.id AS "ID",
		e.color_id AS "ColorID",
		c.color_html AS "Color_HTML",
		e.coordinates AS "Coordinates"
	FROM bk_environment e
	INNER JOIN dir_colors c ON c.id = e.color_id
	INNER JOIN lib_libraries l ON l.id = e.library_id
	WHERE l.id = p_libraryid
	AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: web_bk_environment_modify(integer, integer, integer, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_environment_modify(p_opid integer, p_environmentid integer, p_colorid integer, p_coordinates text, p_libraryid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_opid = 1 AND p_environmentid IS NULL THEN
		INSERT INTO bk_environment (color_id, coordinates, library_id)
		VALUES (p_colorid, p_coordinates, p_libraryid);
	END IF;

	IF p_opid = 2 AND p_environmentid IS NOT NULL THEN
		UPDATE bk_environment
		SET color_id = p_colorid,
			coordinates = p_coordinates
		WHERE id = p_environmentid;
	END IF;

	IF p_opid = 0 AND p_environmentid IS NOT NULL THEN
		DELETE FROM bk_environment
		WHERE id = p_environmentid;
	END IF;
END;
$$;


--
-- Name: web_bk_history_add(integer, uuid, integer, integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_history_add(p_bookid integer, p_bookcellid uuid, p_invstateid integer, p_executorid integer, p_ownerid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF EXISTS (SELECT 1 FROM auth_logins WHERE id = p_executorid AND owner_id = p_ownerid LIMIT 1) THEN
        INSERT INTO bk_history (book_id, book_cell_id, inv_state_id, executor_id)
        VALUES (p_bookid, p_bookcellid, p_invstateid, p_executorid);
    END IF;
END;
$$;


--
-- Name: web_bk_history_get(timestamp with time zone, timestamp with time zone, uuid, uuid, text, integer, integer, integer, integer, boolean, boolean, boolean, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_history_get(p_datefrom timestamp with time zone, p_dateto timestamp with time zone, p_libraryid uuid, p_ownerid uuid, p_searchstring text DEFAULT NULL::text, p_sortid integer DEFAULT 0, p_sortdirection integer DEFAULT 0, p_rowsonpage integer DEFAULT 20, p_rowsoffset integer DEFAULT 0, p_invstate1 boolean DEFAULT false, p_invstate2 boolean DEFAULT false, p_invstate3 boolean DEFAULT false, p_invstate4 boolean DEFAULT false) RETURNS TABLE("ID" integer, "BookID" integer, "BookCode" text, "BookName" text, "DateCreated" timestamp with time zone, "BookCellID" uuid, "BookCellNumber" integer, "BookShelfName" text, "BookCaseName" text, "PlaceFullName" text, "StateID" integer, "StateName" text, "InvStateID" integer, "InvStateName" text, "ExecutorID" integer, "ExecutorName" text, "BooksNewCount" bigint, "BooksFoundCount" bigint, "BooksNotFoundCount" bigint, "BooksReplacedCount" bigint, "QRCodesCount" bigint, "BooksSearchCount" bigint, "RowsCount" bigint)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_search text;
    v_datefrom date;
    v_dateto date;
BEGIN
    -- Нормализация
    v_search := COALESCE(p_searchstring, '');
    v_datefrom := p_datefrom::date;
    v_dateto := p_dateto::date;

    RETURN QUERY
    SELECT
        h.id,
        h.book_id,
        b.book_code,
        b.book_name,
        h.date_created,
        h.book_cell_id,
        bcl.number,
        bs.name,
        bc.name,
        bcs.name || ' > ' || bc.name || ' > ' || bs.name || ' > ' || bcl.number::text,
        bb.state_id,
        bst.name,
        h.inv_state_id,
        bis.name,
        h.executor_id,
        COALESCE(l.last_name, '') || ' ' || LEFT(COALESCE(l.first_name, ''), 1) || '.' || LEFT(COALESCE(l.second_name, ''), 1) || '.',
        -- Статистика по инвентарным статусам
        SUM(CASE WHEN h.inv_state_id = 1 THEN 1 ELSE 0 END) OVER (),
        SUM(CASE WHEN h.inv_state_id = 2 THEN 1 ELSE 0 END) OVER (),
        SUM(CASE WHEN h.inv_state_id = 3 THEN 1 ELSE 0 END) OVER (),
        SUM(CASE WHEN h.inv_state_id = 4 THEN 1 ELSE 0 END) OVER (),
        -- QR-коды (event_id = 53)
        (SELECT COUNT(seh.id)
         FROM mon_stations_event_history seh
         INNER JOIN mon_stations s ON s.id = seh.station_id
         WHERE seh.event_id = 53
           AND s.library_id = p_libraryid
           AND seh.date_event >= v_datefrom
           AND seh.date_event < (v_dateto + interval '1 day')),
        -- Поиск книг (event_id = 54)
        (SELECT COUNT(seh.id)
         FROM mon_stations_event_history seh
         INNER JOIN mon_stations s ON s.id = seh.station_id
         WHERE seh.event_id = 54
           AND s.library_id = p_libraryid
           AND seh.date_event >= v_datefrom
           AND seh.date_event < (v_dateto + interval '1 day')),
        -- Общее число строк
        COUNT(1) OVER ()
    FROM bk_history h
    INNER JOIN lib_books b ON b.id = h.book_id
    INNER JOIN bk_books bb ON bb.book_id = b.id
    INNER JOIN bk_book_cells bcl ON bcl.id = h.book_cell_id
    INNER JOIN bk_book_shelves bs ON bs.id = bcl.book_shelf_id
    INNER JOIN bk_book_cases bc ON bc.id = bs.book_case_id
    INNER JOIN bk_book_case_sections bcs ON bcs.id = bc.book_case_section_id
    INNER JOIN bk_book_inv_states bis ON bis.id = h.inv_state_id
    INNER JOIN bk_book_states bst ON bst.id = bb.state_id
    INNER JOIN auth_logins l ON l.id = h.executor_id
    WHERE bc.library_id = p_libraryid
      AND p_ownerid = (SELECT owner_id FROM lib_libraries WHERE id = p_libraryid LIMIT 1)
      AND h.date_created::date BETWEEN v_datefrom AND v_dateto
      -- Фильтр по инвентарным статусам
      AND (
          (NOT p_invstate1 AND NOT p_invstate2 AND NOT p_invstate3 AND NOT p_invstate4)
          OR (h.inv_state_id = 1 AND p_invstate1)
          OR (h.inv_state_id = 2 AND p_invstate2)
          OR (h.inv_state_id = 3 AND p_invstate3)
          OR (h.inv_state_id = 4 AND p_invstate4)
      )
      -- Поиск
      AND (
          v_search = ''
          OR b.book_name ILIKE '%' || v_search || '%'
          OR b.book_code ILIKE '%' || v_search || '%'
          OR bc.name ILIKE '%' || v_search || '%'
          OR bs.name ILIKE '%' || v_search || '%'
          OR bcl.number::text ILIKE '%' || v_search || '%'
          OR bis.name ILIKE '%' || v_search || '%'
          OR l.last_name ILIKE '%' || v_search || '%'
          OR l.first_name ILIKE '%' || v_search || '%'
          OR l.second_name ILIKE '%' || v_search || '%'
      )
    ORDER BY
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN h.date_created END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN h.date_created END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN b.book_name END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN b.book_name END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN b.book_code END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN b.book_code END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN bc.name END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN bc.name END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN bs.name END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN bs.name END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 5 THEN bcl.number END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 5 THEN bcl.number END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 6 THEN bis.name END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 6 THEN bis.name END DESC,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 7 THEN l.last_name END ASC,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 7 THEN l.last_name END DESC,
        h.date_created DESC
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_bk_station_coordinates_set(uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_bk_station_coordinates_set(p_stationid uuid, p_coordinates text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	UPDATE mon_stations
	SET coordinates = p_coordinates
	WHERE id = p_stationid;
END;
$$;


--
-- Name: web_book_modify(integer, text, text, text, text, boolean, bytea, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_book_modify(p_bookid integer, p_bookcode text, p_bookname text, p_authorsname text, p_description text, p_isspecial boolean, p_cover bytea, p_libraryid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_bookid IS NULL AND NOT EXISTS (SELECT 1 FROM lib_books WHERE book_code = p_bookcode AND library_id = p_libraryid) THEN
		INSERT INTO lib_books (
			book_code, 
			book_name, 
			authors_name, 
			description, 
			is_special, 
			library_id
		)
		VALUES (
			p_bookcode, 
			p_bookname, 
			p_authorsname, 
			p_description, 
			p_isspecial, 
			p_libraryid
		);
		
		SELECT LASTVAL() INTO p_bookid;
		
		IF p_cover IS NOT NULL THEN
			INSERT INTO lib_books_covers (
				book_id, 
				cover
			)
			VALUES (
				p_bookid, 
				p_cover
			);
		END IF;
	END IF;
	
	IF p_bookid IS NOT NULL AND p_bookid != -1 THEN
		UPDATE lib_books
		SET 
			book_code = p_bookcode,
			book_name = p_bookname,
			authors_name = p_authorsname,
			description = p_description,
			is_special = p_isspecial,
			library_id = p_libraryid
		WHERE 
			id = p_bookid;
		
		IF p_cover IS NULL THEN
			DELETE FROM lib_books_covers 
			WHERE book_id = p_bookid;
		ELSIF p_cover IS NOT NULL THEN
			IF EXISTS (SELECT 1 FROM lib_books_covers WHERE book_id = p_bookid) THEN
				UPDATE lib_books_covers
				SET cover = p_cover
				WHERE book_id = p_bookid;
			ELSE
				INSERT INTO lib_books_covers (
					book_id, 
					cover
				)
				VALUES (
					p_bookid, 
					p_cover
				);
			END IF;
		END IF;
	END IF;
	
	IF p_bookid = -1 THEN
		DELETE FROM lib_books_covers 
		WHERE book_id IN (SELECT id FROM lib_books WHERE book_code = p_bookcode AND library_id = p_libraryid);

		DELETE FROM mon_bookshelves_stat
		WHERE book_id IN (SELECT id FROM lib_books WHERE book_code = p_bookcode AND library_id = p_libraryid);

		DELETE FROM bk_history
		WHERE book_id IN (SELECT id FROM lib_books WHERE book_code = p_bookcode AND library_id = p_libraryid);

		DELETE FROM bk_books
		WHERE book_id IN (SELECT id FROM lib_books WHERE book_code = p_bookcode AND library_id = p_libraryid);
		
		DELETE FROM lib_books
		WHERE id IN (SELECT id FROM lib_books WHERE book_code = p_bookcode AND library_id = p_libraryid);
	END IF;
END;
$$;


--
-- Name: web_book_search(text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_book_search(p_searchstring text, p_libraryid uuid) RETURNS TABLE("BookID" integer, "BookName" text, "BookCellID" uuid, "BookCellNumber" integer, "BookShelfID" integer, "BookShelfName" text, "BookCaseID" integer, "BookCaseName" text, "BookCaseSectionID" integer, "BookCaseSectionName" text, "SmartShelfName" text)
    LANGUAGE plpgsql
    AS $_$
DECLARE
    where_clause TEXT := '';
    word TEXT;
    search_string_with_spaces TEXT := p_searchstring || ' ';
    query TEXT;
BEGIN
    -- Разбиваем поисковую строку на слова
    WHILE LENGTH(search_string_with_spaces) > 0 LOOP
        word := LEFT(search_string_with_spaces, POSITION(' ' IN search_string_with_spaces) - 1);
        search_string_with_spaces := SUBSTRING(search_string_with_spaces, POSITION(' ' IN search_string_with_spaces) + 1);
        word := TRIM(word);
        
        IF LENGTH(word) > 0 THEN
            IF LENGTH(where_clause) > 0 THEN
                where_clause := where_clause || ' AND ';
            END IF;
            where_clause := where_clause || ' lb.book_name LIKE ''%' || word || '%''';
        END IF;
    END LOOP;

    -- Если строка поиска пустая, возвращаем пустой результат
    IF LENGTH(where_clause) = 0 THEN
        RETURN QUERY SELECT
            0 AS "BookID",
            NULL AS "BookName",
            NULL AS "BookCellID",
            0 AS "BookCellNumber",
            0 AS "BookShelfID",
            NULL AS "BookShelfName",
            0 AS "BookCaseID",
            NULL AS "BookCaseName",
            0 AS "BookCaseSectionID",
            NULL AS "BookCaseSectionName",
            NULL AS "SmartShelfName"
        WHERE FALSE; -- Пустой набор данных
        RETURN;
    END IF;

    query := '
    SELECT
        b.book_id AS "BookID",
        lb.book_name AS "BookName",
        b.cell_id AS "BookCellID",
        bcl.number AS "BookCellNumber",
        bs.id AS "BookShelfID",
        bs.name AS "BookShelfName",
        bc.id AS "BookCaseID",
        bc.name AS "BookCaseName",
        bcs.id AS "BookCaseSectionID",
        bcs.name AS "BookCaseSectionName",
        CASE WHEN bss.is_online = 1 THEN s.name ELSE NULL END AS "SmartShelfName"
    FROM bk_books b
    INNER JOIN lib_books lb ON lb.id = b.book_id
    INNER JOIN bk_bookcells bcl ON b.cell_id = bcl.id
    INNER JOIN bk_bookshelves bs ON bs.id = bcl.book_shelf_id
    INNER JOIN bk_bookcases bc ON bc.id = bs.book_case_id
    INNER JOIN bk_bookcase_sections bcs ON bcs.id = bc.book_case_section_id
    LEFT JOIN mon_bookshelves_stat bss ON bss.book_id = b.book_id
    LEFT JOIN mon_stations s ON s.id = bss.station_id
    WHERE
        (' || where_clause || ') 
        AND bc.library_id = $1
        AND (b.state_id = 1 OR (b.state_id = 5 AND bss.is_online = 1))';

    RETURN QUERY EXECUTE query USING p_libraryid;
END;
$_$;


--
-- Name: web_books_on_vending_station_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_books_on_vending_station_get(p_stationid uuid) RETURNS TABLE("ID" integer, "VendingStationID" uuid, "CellNumber" integer, "BookName" text, "BookCode" text, "MasterID" integer, "UserCode" text, "DateIn" timestamp with time zone, "BookRate" integer, "BookRateCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    library_id UUID;
BEGIN
    library_id := (SELECT library_id FROM mon_stations WHERE id = p_stationid LIMIT 1);
    RETURN QUERY
    SELECT 
        vb.id AS "ID",
        vb.vending_station_id AS "VendingStationID",
        vb.cell_number AS "CellNumber",
        vb.book_name AS "BookName",
        vb.book_code AS "BookCode",
        vb.master_id AS "MasterID",
        vb.user_code AS "UserCode",
        vb.date_in AS "DateIn",
        COALESCE((SELECT AVG(rate)::INTEGER FROM lib_book_user_ratings WHERE book_id = b.id), 0) AS "BookRate",  -- ← приведение к INTEGER
        (SELECT COUNT(id) FROM lib_book_user_ratings WHERE book_id = b.id) AS "BookRateCount"
    FROM sk_vending_books vb
    LEFT JOIN lib_books b ON b.book_code = vb.book_code AND b.library_id = library_id
    WHERE vb.vending_station_id = p_stationid AND vb.date_out IS NULL;
END;
$$;


--
-- Name: web_colors_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_colors_get() RETURNS TABLE("ID" integer, "Color_HTML" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT id AS "ID", color_html AS "Color_HTML" FROM dir_colors;
END;
$$;


--
-- Name: web_desktop_device_current_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_desktop_device_current_get(p_deviceid uuid, p_ownerid uuid) RETURNS TABLE("LibraryName" text, "WorkplaceName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT l.name AS "LibraryName",
		d.name AS "WorkplaceName"
	FROM mon_devices d
	INNER JOIN lib_libraries l ON l.id = d.library_id
	WHERE d.id = p_deviceid AND l.owner_id = p_ownerid;
END;
$$;


--
-- Name: web_desktop_device_modify(integer, uuid, text, integer, text, text, uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_desktop_device_modify(p_opid integer, p_id uuid, p_name text, p_typeid integer, p_cfg text, p_cfg2 text, p_libraryid uuid, INOUT new_id uuid DEFAULT NULL::uuid) RETURNS uuid
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_opid = 1 THEN
        new_id := gen_random_uuid();
        INSERT INTO mon_devices (id, name, type_id, cfg, cfg2, library_id)
        VALUES (new_id, p_name, p_typeid, p_cfg, p_cfg2, p_libraryid);
    ELSIF p_opid = 2 THEN
        UPDATE mon_devices
        SET name = p_name,
            type_id = p_typeid,
            cfg = p_cfg,
            cfg2 = p_cfg2,
            library_id = p_libraryid
        WHERE id = p_id;
		new_id := p_id;

		ELSIF p_opid = 0 THEN
			DELETE FROM mon_device_data 
			WHERE device_id = p_id;
	
			DELETE FROM mon_devices
			WHERE id = p_id;
			new_id := p_id;
    END IF;
END;
$$;


--
-- Name: web_desktop_device_monitoring_get(uuid, timestamp with time zone, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_desktop_device_monitoring_get(p_deviceid uuid, p_selecteddate timestamp with time zone, p_ownerid uuid) RETURNS TABLE("DateValue" timestamp with time zone, "HourValue" integer, "DeviceID" uuid, "DeviceName" text, "LibraryID" uuid, "DeviceTypeID" integer, "DeviceCFGTypeID" integer, "SoftOnlineCount" integer, "DeviceOKCount" integer, "DeviceErrorCount" integer, "DeviceOKCount_Percent" numeric, "DeviceStateID" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT p_selecteddate AS "DateValue", h.hour_value AS "HourValue", d.*
    ,COALESCE(dd.soft_online_count, 0) AS "SoftOnlineCount"
    ,COALESCE(dd.device_ok_count, 0) AS "DeviceOKCount"
    ,COALESCE(dd.device_error_count, 0) AS "DeviceErrorCount"
    ,CASE WHEN MAX(dd.device_ok_count) OVER (PARTITION BY dd.date_value, dd.device_id)*100<>0 
    THEN
      ROUND(CAST(dd.device_ok_count AS NUMERIC) / MAX(dd.device_ok_count) OVER (PARTITION BY dd.date_value, dd.device_id)*100, 0) 
    ELSE 0 END
      AS "DeviceOKCount_Percent",
      CASE WHEN COALESCE(dd.device_ok_count, 0) = 0 AND COALESCE(dd.device_error_count, 0) = 0 THEN 1 ELSE
      CASE WHEN COALESCE(dd.device_ok_count, 0) > 0 AND COALESCE(dd.device_error_count, 0) = 0 THEN 4 ELSE
      CASE WHEN COALESCE(dd.device_ok_count, 0) = 0 AND COALESCE(dd.device_error_count, 0) > 0 THEN 3 ELSE
      CASE WHEN COALESCE(dd.device_ok_count, 0) > 0 AND COALESCE(dd.device_error_count, 0) > 0 AND (dd.device_ok_count/dd.device_error_count) < 0.9 THEN 2 ELSE 4
    END END END END  AS "DeviceStateID"
  FROM dir_hours_day h
  INNER JOIN
  (
    SELECT
     d.id AS "DeviceID"
    ,d.name AS "DeviceName"
    ,d.library_id AS "LibraryID"
    ,d.type_id AS "DeviceTypeID"
    ,dt.id AS "DeviceCFGTypeID"
    FROM mon_devices d
    INNER JOIN mon_device_cfg_types dt ON dt.id = d.type_id
    WHERE d.id = p_deviceid AND d.library_id IN (SELECT id FROM lib_libraries WHERE owner_id = p_ownerid)
    LIMIT 1
    ) d
  ON d."DeviceID" = p_deviceid
  LEFT JOIN
  mon_device_data dd
  ON h.hour_value = dd.hour_value AND dd.date_value = p_selecteddate AND dd.device_id = p_deviceid
  ORDER BY h.hour_value;
END;
$$;


--
-- Name: web_desktop_devices_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_desktop_devices_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "DateCreated" timestamp with time zone, "LastUpdate" timestamp with time zone, "IsOnline" integer, "TypeCFGID" integer, "TypeCFGName" text, "TypeID" integer, "TypeName" text, "CFG" text, "CFG2" text, "LibraryID" uuid)
    LANGUAGE plpgsql
    AS $$
BEGIN
  RETURN QUERY
  SELECT
    d.id AS "ID",  -- предполагается NOT NULL в mon_devices
    d.name AS "Name",
    d.date_created AS "DateCreated",  -- NOT NULL
    d.last_update AS "LastUpdate",
    -- Гарантируем, что IsOnline НИКОГДА не NULL: даже если last_update IS NULL → считаем оффлайн (0)
    CASE 
      WHEN d.last_update IS NULL THEN 0
      WHEN EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - d.last_update)) / 60 < 2 THEN 1
      ELSE 0
    END AS "IsOnline",
    d.type_id AS "TypeCFGID",  -- должен быть NOT NULL в mon_devices
    dc.name AS "TypeCFGName",
    dt.id AS "TypeID",  -- из INNER JOIN → NOT NULL
    dt.name AS "TypeName",
    d.cfg AS "CFG",
    d.cfg2 AS "CFG2",
    d.library_id AS "LibraryID"  -- NOT NULL
  FROM mon_devices d
  INNER JOIN mon_device_types dt ON dt.id = d.type_id          -- INNER → dt.id гарантирован
  INNER JOIN mon_device_cfg_types dc ON dc.id = dt.device_cfg_type_id
  INNER JOIN lib_libraries l ON l.id = d.library_id
  WHERE 
    (p_libraryid IS NOT NULL AND l.id = p_libraryid) 
    OR 
    (p_libraryid IS NULL AND l.owner_id = p_ownerid)
  ORDER BY d.date_created DESC;
END;
$$;


--
-- Name: web_desktop_devices_history_get(uuid, uuid, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_desktop_devices_history_get(p_libraryid uuid, p_ownerid uuid, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("DeviceID" uuid, "DeviceName" text, "LastDeviceActivity" timestamp with time zone, "LastSoftwareActivity" timestamp with time zone, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        d.id AS "DeviceID",
        d.name AS "DeviceName",
        (SELECT dd.date_value + INTERVAL '1 hour' * dd.hour_value 
         FROM mon_device_data dd 
         WHERE dd.device_id = d.id AND dd.device_ok_count > 0 
         ORDER BY dd.id DESC LIMIT 1) AS "LastDeviceActivity",
        (SELECT dd.date_value + INTERVAL '1 hour' * dd.hour_value 
         FROM mon_device_data dd 
         WHERE dd.device_id = d.id AND dd.soft_online_count > 0 
         ORDER BY dd.id DESC LIMIT 1) AS "LastSoftwareActivity",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM mon_devices d
    WHERE ((p_libraryid IS NOT NULL AND d.library_id = p_libraryid) 
           OR (p_libraryid IS NULL AND d.library_id IN (SELECT id FROM lib_libraries WHERE owner_id = p_ownerid)))
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN d.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN d.name END DESC NULLS FIRST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN 
            (SELECT dd.date_value + INTERVAL '1 hour' * dd.hour_value 
             FROM mon_device_data dd 
             WHERE dd.device_id = d.id AND dd.device_ok_count > 0 
             ORDER BY dd.id DESC LIMIT 1)
        END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN 
            (SELECT dd.date_value + INTERVAL '1 hour' * dd.hour_value 
             FROM mon_device_data dd 
             WHERE dd.device_id = d.id AND dd.device_ok_count > 0 
             ORDER BY dd.id DESC LIMIT 1)
        END DESC NULLS FIRST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN 
            (SELECT dd.date_value + INTERVAL '1 hour' * dd.hour_value 
             FROM mon_device_data dd 
             WHERE dd.device_id = d.id AND dd.soft_online_count > 0 
             ORDER BY dd.id DESC LIMIT 1)
        END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN 
            (SELECT dd.date_value + INTERVAL '1 hour' * dd.hour_value 
             FROM mon_device_data dd 
             WHERE dd.device_id = d.id AND dd.soft_online_count > 0 
             ORDER BY dd.id DESC LIMIT 1)
        END DESC NULLS FIRST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_desktop_devices_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_desktop_devices_types_get() RETURNS TABLE("ID" integer, "Name" text, "TypeCFGID" integer, "TypeName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
		SELECT
			d.id AS "ID",
			d.name AS "Name",
			dc.id AS "TypeCFGID",
			dc.name AS "TypeName"
		FROM mon_device_types d
		INNER JOIN mon_device_cfg_types dc ON dc.id = d.device_cfg_type_id;
END;
$$;


--
-- Name: web_gate_alarms_get(uuid, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gate_alarms_get(p_gateid uuid, p_startdate timestamp with time zone, p_enddate timestamp with time zone) RETURNS TABLE("Date" timestamp with time zone, "AlarmsCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    start_date DATE;
    end_date DATE;
BEGIN
    start_date := p_startdate::DATE;
    end_date := p_enddate::DATE;

    IF start_date > end_date THEN
        RETURN;
    END IF;

    IF start_date = end_date THEN
        RETURN QUERY
        SELECT 
            (start_date + INTERVAL '1 hour' * h.hour_value)::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(COUNT(e.gate_id), 0)::integer AS "AlarmsCount"
        FROM dir_hours_day h
        LEFT JOIN (
            SELECT gate_id,
                   EXTRACT(HOUR FROM date_value) AS hour_field
            FROM mon_gates_eas
            WHERE gate_id = p_gateid 
              AND date_value::DATE = start_date
        ) e ON e.hour_field = h.hour_value
        GROUP BY h.hour_value
        ORDER BY "Date";

    ELSIF EXTRACT(YEAR FROM start_date) <> EXTRACT(YEAR FROM end_date) THEN
        RETURN QUERY
        SELECT 
            (y.year_start)::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(e.gate_count, 0)::integer AS "AlarmsCount"
        FROM (
            SELECT (start_date + (n || ' years')::interval)::date AS year_start
            FROM generate_series(0, EXTRACT(YEAR FROM age(end_date, start_date))::int) AS g(n)
        ) y
        LEFT JOIN (
            SELECT 
                DATE_TRUNC('year', date_value)::date AS year_field,
                COUNT(gate_id)::integer AS gate_count
            FROM mon_gates_eas
            WHERE gate_id = p_gateid
              AND date_value::date BETWEEN start_date AND end_date
            GROUP BY DATE_TRUNC('year', date_value)::date
        ) e ON e.year_field = y.year_start
        ORDER BY "Date";

    ELSIF EXTRACT(MONTH FROM start_date) <> EXTRACT(MONTH FROM end_date) THEN
        RETURN QUERY
        SELECT 
            DATE_TRUNC('month', d.day_value)::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(e.gate_count, 0)::integer AS "AlarmsCount"
        FROM generate_series(
            DATE_TRUNC('month', start_date),
            DATE_TRUNC('month', end_date),
            INTERVAL '1 month'
        ) AS d(day_value)
        LEFT JOIN (
            SELECT 
                DATE_TRUNC('month', date_value)::date AS month_field,
                COUNT(gate_id)::integer AS gate_count
            FROM mon_gates_eas
            WHERE gate_id = p_gateid
              AND date_value::date BETWEEN start_date AND end_date
            GROUP BY DATE_TRUNC('month', date_value)::date
        ) e ON e.month_field = DATE_TRUNC('month', d.day_value)::date
        ORDER BY "Date";

    ELSE
        -- Для запросов в пределах одного месяца - группировка по дням
        RETURN QUERY
        SELECT 
            d.day_value::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(e.gate_count, 0)::integer AS "AlarmsCount"
        FROM (
            SELECT generate_series(start_date, end_date, INTERVAL '1 day')::date AS day_value
        ) d
        LEFT JOIN (
            SELECT 
                date_value::date AS date_field,
                COUNT(gate_id)::integer AS gate_count
            FROM mon_gates_eas
            WHERE gate_id = p_gateid
              AND date_value >= p_startdate 
              AND date_value <= p_enddate
            GROUP BY date_value::date
        ) e ON e.date_field = d.day_value
        ORDER BY d.day_value;
    END IF;
END;
$$;


--
-- Name: web_gate_counters_get(uuid, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gate_counters_get(p_gateid uuid, p_startdate timestamp with time zone, p_enddate timestamp with time zone) RETURNS TABLE("Date" timestamp with time zone, "ValueIn" integer, "ValueOut" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    start_date DATE;
    end_date DATE;
BEGIN
    start_date := p_startdate::DATE;
    end_date := p_enddate::DATE;

    IF start_date > end_date THEN
        RETURN;
    END IF;

    IF start_date = end_date THEN
        -- Часовая группировка за один день
        RETURN QUERY
        SELECT
            (start_date + INTERVAL '1 hour' * h.hour_value)::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(SUM(c.value_in), 0)::integer AS "ValueIn",
            COALESCE(SUM(c.value_out), 0)::integer AS "ValueOut"
        FROM dir_hours_day h
        LEFT JOIN mon_gates_counters c
            ON c.gate_id = p_gateid
            AND c.date_value >= (start_date + INTERVAL '1 hour' * h.hour_value)
            AND c.date_value < (start_date + INTERVAL '1 hour' * (h.hour_value + 1))
        GROUP BY h.hour_value
        ORDER BY "Date";

    ELSIF EXTRACT(YEAR FROM start_date) <> EXTRACT(YEAR FROM end_date) THEN
        -- Годовая группировка
        RETURN QUERY
        SELECT
            y.year_start::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(e.total_in, 0)::integer AS "ValueIn",
            COALESCE(e.total_out, 0)::integer AS "ValueOut"
        FROM (
            SELECT (start_date + (n || ' years')::interval)::date AS year_start
            FROM generate_series(0, EXTRACT(YEAR FROM age(end_date, start_date))::int) AS g(n)
        ) y
        LEFT JOIN (
            SELECT
                DATE_TRUNC('year', date_value)::date AS year_field,
                SUM(value_in)::bigint AS total_in,
                SUM(value_out)::bigint AS total_out
            FROM mon_gates_counters
            WHERE gate_id = p_gateid
              AND date_value::date BETWEEN start_date AND end_date
            GROUP BY DATE_TRUNC('year', date_value)::date
        ) e ON e.year_field = y.year_start
        ORDER BY "Date";

    ELSIF EXTRACT(MONTH FROM start_date) <> EXTRACT(MONTH FROM end_date) THEN
        -- Месячная группировка
        RETURN QUERY
        SELECT
            DATE_TRUNC('month', d.month_value)::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(e.total_in, 0)::integer AS "ValueIn",
            COALESCE(e.total_out, 0)::integer AS "ValueOut"
        FROM generate_series(
            DATE_TRUNC('month', start_date),
            DATE_TRUNC('month', end_date),
            INTERVAL '1 month'
        ) AS d(month_value)
        LEFT JOIN (
            SELECT
                DATE_TRUNC('month', date_value)::date AS month_field,
                SUM(value_in)::bigint AS total_in,
                SUM(value_out)::bigint AS total_out
            FROM mon_gates_counters
            WHERE gate_id = p_gateid
              AND date_value::date BETWEEN start_date AND end_date
            GROUP BY DATE_TRUNC('month', date_value)::date
        ) e ON e.month_field = DATE_TRUNC('month', d.month_value)::date
        ORDER BY "Date";

    ELSE
        -- Дневная группировка (в рамках одного месяца)
        RETURN QUERY
        SELECT
            d.day_value::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(e.total_in, 0)::integer AS "ValueIn",
            COALESCE(e.total_out, 0)::integer AS "ValueOut"
        FROM (
            SELECT generate_series(start_date, end_date, INTERVAL '1 day')::date AS day_value
        ) d
        LEFT JOIN (
            SELECT
                date_value::date AS date_field,
                SUM(value_in)::bigint AS total_in,
                SUM(value_out)::bigint AS total_out
            FROM mon_gates_counters
            WHERE gate_id = p_gateid
              AND date_value >= p_startdate
              AND date_value <= p_enddate
            GROUP BY date_value::date
        ) e ON e.date_field = d.day_value
        ORDER BY d.day_value;
    END IF;
END;
$$;


--
-- Name: web_gate_history_get(uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gate_history_get(p_gateid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" bigint, "GateID" uuid, "DateValue" timestamp with time zone, "UID" text, "BookName" text, "BookCode" text, "IsBook" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_end_var TIMESTAMP;
    search_string_var TEXT;
BEGIN
    date_end_var := (p_dateend::DATE + INTERVAL '1 day')::TIMESTAMP;
    search_string_var := COALESCE(p_searchstring, '');

    RETURN QUERY
    SELECT 
        g.id::bigint AS "ID",  
        g.gate_id AS "GateID",
        g.date_value AS "DateValue",
        g.uid AS "UID",
        COALESCE(b.book_name, 'Неизвестная книга') AS "BookName",
        b.book_code AS "BookCode",
        b.is_book AS "IsBook",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM mon_gates_eas g
    INNER JOIN mon_gates gg ON gg.id = g.gate_id
    INNER JOIN lib_books b ON b.book_code = g.uid AND b.library_id = gg.library_id
    WHERE g.gate_id = p_gateid 
        AND g.date_value >= p_datestart::DATE
        AND g.date_value < date_end_var  
        AND (
            search_string_var = '' 
            OR b.book_name ILIKE '%' || search_string_var || '%' 
            OR b.book_code ILIKE '%' || search_string_var || '%'
        )
    ORDER BY
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN g.date_value END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN g.date_value END DESC NULLS FIRST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN b.book_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN b.book_name END DESC NULLS FIRST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN b.book_code END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN b.book_code END DESC NULLS FIRST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_gate_modify(uuid, text, text, integer, integer, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gate_modify(p_id uuid, p_name text, p_ip text, p_port integer, p_typeid integer, p_cfgtypeid integer, p_libraryid uuid, p_countername text, p_counterip text, p_counterport integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
DECLARE
    new_id UUID;
BEGIN
	IF (p_id IS NOT NULL) THEN
		UPDATE mon_gates
		SET name = p_name,
			ip = p_ip,
			port = p_port,
			type_id = p_typeid,
			cfg_type_id = p_cfgtypeid,
			library_id = p_libraryid
		WHERE id = p_id;

		IF (p_counterip IS NOT NULL) THEN
			IF EXISTS (SELECT 1 FROM mon_mono_gate_count_devices WHERE gate_id = p_id) THEN
				UPDATE mon_mono_gate_count_devices
				SET name = p_countername,
					ip = p_counterip,
					port = p_counterport
				WHERE gate_id = p_id;
			ELSE
				INSERT INTO mon_mono_gate_count_devices (name, ip, port, gate_id)
				VALUES (p_countername, p_counterip, p_counterport, p_id);
			END IF;
		END IF;
	ELSE
		new_id := gen_random_uuid();
		INSERT INTO mon_gates (id, name, ip, port, type_id, cfg_type_id, library_id)
		VALUES (new_id, p_name, p_ip, p_port, p_typeid, p_cfgtypeid, p_libraryid);

		IF (p_counterip IS NOT NULL) THEN
			INSERT INTO mon_mono_gate_count_devices (name, ip, port, gate_id)
			VALUES (p_countername, p_counterip, p_counterport, new_id);
		END IF;
	END IF;
END;
$$;


--
-- Name: web_gate_monitoring_get(uuid, timestamp with time zone, timestamp with time zone, integer, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gate_monitoring_get(p_gateid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_sortid integer, p_filterid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "GateID" uuid, "DateEvent" timestamp with time zone, "EventID" integer, "EventName" text, "EventTypeID" integer, "EventTypeName" text, "Count" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_end_var TIMESTAMP;
BEGIN
    date_end_var := (p_dateend::DATE + INTERVAL '1 day')::TIMESTAMP;

    RETURN QUERY
    SELECT
        ge.id::integer AS "ID",       -- ← явное приведение к integer
        ge.gate_id AS "GateID",
        ge.date_value AS "DateEvent",
        ge.event_id AS "EventID",
        e.name AS "EventName",
        e.event_type_id AS "EventTypeID",
        et.name AS "EventTypeName",
        ge.count AS "Count",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← приведение к INTEGER
    FROM mon_gates_event_history ge
    INNER JOIN mon_events e ON e.id = ge.event_id
    INNER JOIN mon_event_types et ON et.id = e.event_type_id
    WHERE ge.gate_id = p_gateid
        AND ge.date_value >= p_datestart::DATE
        AND ge.date_value < date_end_var  
        AND (
            p_filterid <= 0 
            OR et.id = p_filterid
        )
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN ge.date_value END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN ge.date_value END DESC NULLS FIRST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN e.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN e.name END DESC NULLS FIRST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_gates_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gates_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "IP" text, "Port" integer, "TypeID" integer, "CFGTypeID" integer, "TypeName" text, "CFGTypeName" text, "LastUpdate" timestamp with time zone, "IsOnline" integer, "LibraryID" uuid, "LibraryName" text, "CounterName" text, "CounterIP" text, "CounterPort" integer, "IsCounterOnline" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        g.id AS "ID",
        g.name AS "Name",
        g.ip AS "IP",
        g.port AS "Port",
        g.type_id AS "TypeID",
        g.cfg_type_id AS "CFGTypeID",
        gt.name AS "TypeName",
        gct.name AS "CFGTypeName",
        g.last_update AS "LastUpdate",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - g.last_update))/60, 3) < 2 THEN 1 ELSE 0 END AS "IsOnline",
        g.library_id AS "LibraryID",
        l.name AS "LibraryName",
        m.name AS "CounterName",
        m.ip AS "CounterIP",
        COALESCE(m.port, 0) AS "CounterPort",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - m.date_last_online))/60, 3) < 2 THEN 1 ELSE 0 END AS "IsCounterOnline"
    FROM mon_gates g
    INNER JOIN mon_gate_types gt ON gt.id = g.type_id
    INNER JOIN mon_gate_cfg_types gct ON gct.id = g.cfg_type_id
    INNER JOIN lib_libraries l ON l.id = g.library_id
    LEFT JOIN LATERAL (
        SELECT m2.name, m2.ip, m2.port, m2.date_last_online
        FROM mon_mono_gate_count_devices m2
        WHERE m2.gate_id = g.id
        LIMIT 1
    ) m ON true
    WHERE (p_libraryid IS NOT NULL AND l.id = p_libraryid) 
       OR (p_libraryid IS NULL AND l.owner_id = p_ownerid);
END;
$$;


--
-- Name: web_gates_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gates_types_get() RETURNS TABLE("TypeID" integer, "TypeName" text, "CFGTypeID" integer, "CFGTypeName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		gt.id AS "TypeID",
		gt.name AS "TypeName",
		gct.id AS "CFGTypeID",
		gct.name AS "CFGTypeName"
	FROM mon_gate_types gt
	INNER JOIN mon_gate_cfg_types gct ON gt.id = gct.type_id;
END;
$$;


--
-- Name: web_gpt_get_analytics(uuid, uuid, date, date, boolean, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_get_analytics(p_libraryid uuid, p_stationid uuid DEFAULT NULL::uuid, p_startdate date DEFAULT NULL::date, p_enddate date DEFAULT NULL::date, p_requesttype boolean DEFAULT NULL::boolean, p_searchstring text DEFAULT NULL::text) RETURNS TABLE("StationID" uuid, "StationName" text, "RequestType" text, "QuestionText" text, "AnswerText" text, "RequestCount" integer, "AvgSessionDuration" integer, "DateCreated" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        vi.station_id AS "StationID",
        s.name AS "StationName",
        CASE WHEN vi.is_template_used = TRUE THEN 'Шаблон' ELSE 'Вопрос' END AS "RequestType",
        COALESCE(t.question_text, vi.request_text) AS "QuestionText",
        COALESCE(t.answer_text, vi.response_text) AS "AnswerText",
        COUNT(*)::INTEGER AS "RequestCount",                     -- ← приведение к INTEGER
        COALESCE(AVG(vi.session_duration)::INTEGER, 0) AS "AvgSessionDuration", -- ← приведение к INTEGER + защита от NULL
        MAX(vi.date_created) AS "DateCreated"
    FROM gpt_voice_interactions vi
    JOIN mon_stations s ON vi.station_id = s.id
    LEFT JOIN gpt_voice_response_templates t ON vi.template_id = t.id AND vi.is_template_used = TRUE
    WHERE vi.library_id = p_libraryid
        AND (p_stationid IS NULL OR vi.station_id = p_stationid)
        AND (p_startdate IS NULL OR vi.date_created::DATE >= p_startdate)
        AND (p_enddate IS NULL OR vi.date_created::DATE <= p_enddate)
        AND (p_requesttype IS NULL OR vi.is_template_used = p_requesttype)
        AND (p_searchstring IS NULL OR vi.request_text ILIKE '%' || p_searchstring || '%')
    GROUP BY vi.station_id, s.name, vi.is_template_used, COALESCE(t.question_text, vi.request_text), COALESCE(t.answer_text, vi.response_text)
    ORDER BY "RequestCount" DESC, s.name, "QuestionText";
END;
$$;


--
-- Name: web_gpt_get_languages(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_get_languages() RETURNS TABLE("ID" integer, "Name" text, "Code" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        l.id::int AS "ID",
        l.name::text AS "Name",
        l.code::text AS "Code"
    FROM gpt_languages l
    ORDER BY l.name;
END;
$$;


--
-- Name: web_gpt_get_template_stations(uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_get_template_stations(p_libraryid uuid, p_templateid integer) RETURNS TABLE("StationID" uuid, "StationName" text, "IsActive" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        s.id AS "StationID",
        s.name AS "StationName",
        COALESCE(ts.is_active, FALSE) AS "IsActive"
    FROM mon_stations s
    LEFT JOIN gpt_voice_template_stations ts ON s.id = ts.station_id AND ts.template_id = p_templateid
    WHERE s.library_id = p_libraryid
    ORDER BY s.name;
END;
$$;


--
-- Name: web_gpt_get_top_queries(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_get_top_queries(p_libraryid uuid, p_stationid uuid DEFAULT NULL::uuid, p_startdate date DEFAULT NULL::date, p_enddate date DEFAULT NULL::date) RETURNS TABLE("QuestionText" text, "RequestCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        COALESCE(t.question_text, vi.request_text) AS "QuestionText",
        COUNT(*)::INTEGER AS "RequestCount"  -- ← явное приведение к INTEGER
    FROM gpt_voice_interactions vi
    LEFT JOIN gpt_voice_response_templates t ON vi.template_id = t.id AND vi.is_template_used = TRUE
    WHERE vi.library_id = p_libraryid
        AND (p_stationid IS NULL OR vi.station_id = p_stationid)
        AND (p_startdate IS NULL OR vi.date_created::DATE >= p_startdate)
        AND (p_enddate IS NULL OR vi.date_created::DATE <= p_enddate)
    GROUP BY COALESCE(t.question_text, vi.request_text)
    ORDER BY "RequestCount" DESC
    LIMIT 5;
END;
$$;


--
-- Name: web_gpt_get_voice_assistant_settings(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_get_voice_assistant_settings(p_libraryid uuid) RETURNS TABLE("StationID" uuid, "StationName" text, "IsEnabled" boolean, "VoiceTypeID" integer, "VoiceTypeName" text, "LanguageID" integer, "LanguageName" text, "LastUpdated" timestamp with time zone, "TypeID" integer, "TypeName" text, "SN" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        s.id AS "StationID",
        s.name AS "StationName",
        vas.is_enabled AS "IsEnabled",
        vas.voice_type_id AS "VoiceTypeID",
        vt.name AS "VoiceTypeName",
        vas.language_id AS "LanguageID",
        l.name AS "LanguageName",
        vas.last_updated AS "LastUpdated",
        st.id AS "TypeID",
        st.name AS "TypeName",
        s.sn AS "SN"
    FROM mon_stations s
    LEFT JOIN gpt_voice_assistant_settings vas ON vas.station_id = s.id
    LEFT JOIN gpt_voice_types vt ON vas.voice_type_id = vt.id
    LEFT JOIN gpt_languages l ON vas.language_id = l.id
    LEFT JOIN mon_station_types st ON st.id = s.station_type_id
    WHERE s.library_id = p_libraryid
    ORDER BY s.name;
END;
$$;


--
-- Name: web_gpt_get_voice_response_templates(uuid, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_get_voice_response_templates(p_libraryid uuid, p_isactive boolean DEFAULT NULL::boolean) RETURNS TABLE("ID" integer, "LibraryID" uuid, "QuestionText" text, "AnswerText" text, "IsActive" boolean, "FontSize" integer, "FontColor" text, "BorderColor" text, "DateCreated" timestamp with time zone, "DateUpdated" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        t.id AS "ID",
        t.library_id AS "LibraryID",
        t.question_text AS "QuestionText",
        t.answer_text AS "AnswerText",
        t.is_active AS "IsActive",
        t.font_size AS "FontSize",
        t.font_color AS "FontColor",
        t.border_color AS "BorderColor",
        t.date_created AS "DateCreated",
        t.date_updated AS "DateUpdated"
    FROM gpt_voice_response_templates t
    WHERE t.library_id = p_libraryid
        AND (p_isactive IS NULL OR t.is_active = p_isactive)
    ORDER BY t.question_text;
END;
$$;


--
-- Name: web_gpt_get_voice_types(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_get_voice_types() RETURNS TABLE("ID" integer, "Name" text, "Description" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        vt.id::int AS "ID",
        vt.name::text AS "Name",
        vt.description::text AS "Description"
    FROM gpt_voicetypes vt
    ORDER BY vt.name;
END;
$$;


--
-- Name: web_gpt_update_template_stations(integer, uuid, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_update_template_stations(p_templateid integer, p_stationid uuid, p_isactive boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_isactive = TRUE THEN
        IF NOT EXISTS (SELECT 1 FROM gpt_voice_template_stations WHERE template_id = p_templateid AND station_id = p_stationid) THEN
            INSERT INTO gpt_voice_template_stations (template_id, station_id, is_active)
            VALUES (p_templateid, p_stationid, p_isactive);
        ELSE
            UPDATE gpt_voice_template_stations
            SET is_active = p_isactive
            WHERE template_id = p_templateid AND station_id = p_stationid;
        END IF;
    ELSE
        DELETE FROM gpt_voice_template_stations
        WHERE template_id = p_templateid AND station_id = p_stationid;
    END IF;
END;
$$;


--
-- Name: web_gpt_update_voice_assistant_settings(uuid, boolean, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_update_voice_assistant_settings(p_stationid uuid, p_isenabled boolean, p_voicetypeid integer, p_languageid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF EXISTS (SELECT 1 FROM gpt_voice_assistant_settings WHERE station_id = p_stationid) THEN
        UPDATE gpt_voice_assistant_settings
        SET 
            is_enabled = p_isenabled,
            voice_type_id = p_voicetypeid,
            language_id = p_languageid,
            last_updated = CURRENT_TIMESTAMP
        WHERE station_id = p_stationid;
    ELSE
        INSERT INTO gpt_voice_assistant_settings (station_id, is_enabled, voice_type_id, language_id, is_service_alive, last_updated)
        VALUES (p_stationid, p_isenabled, p_voicetypeid, p_languageid, FALSE, CURRENT_TIMESTAMP);
    END IF;
END;
$$;


--
-- Name: web_gpt_update_voice_response_template(integer, uuid, text, text, boolean, integer, text, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_gpt_update_voice_response_template(p_templateid integer, p_libraryid uuid, p_questiontext text, p_answertext text, p_isactive boolean DEFAULT true, p_fontsize integer DEFAULT 16, p_fontcolor text DEFAULT '#000000'::text, p_bordercolor text DEFAULT '#000000'::text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_templateid IS NULL THEN
        INSERT INTO gpt_voice_response_templates (
            library_id, question_text, answer_text, is_active, date_created, date_updated,
            font_size, font_color, border_color
        )
        VALUES (
            p_libraryid, p_questiontext, p_answertext, p_isactive, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
            p_fontsize, p_fontcolor, p_bordercolor
        );
    ELSIF p_templateid = -1 THEN
        DELETE FROM gpt_voice_response_templates
        WHERE id = p_templateid AND library_id = p_libraryid;
    ELSE
        UPDATE gpt_voice_response_templates
        SET 
            question_text = p_questiontext,
            answer_text = p_answertext,
            is_active = p_isactive,
            font_size = p_fontsize,
            font_color = p_fontcolor,
            border_color = p_bordercolor,
            date_updated = CURRENT_TIMESTAMP
        WHERE id = p_templateid AND library_id = p_libraryid;
    END IF;
END;
$$;


--
-- Name: web_kpi_eployees_get(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_eployees_get(p_libraryid uuid, p_ownerid uuid, p_searchstring text) RETURNS TABLE("LoginID" integer, "EployeeName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT 
		al.id AS "LoginID",
		fn_fio_get(al.first_name, al.last_name, al.second_name) AS "EployeeName"
	FROM auth_logins al
	INNER JOIN auth_login_roles lr ON lr.login_id = al.id
	INNER JOIN auth_role_types rt ON rt.id = lr.role_type_id
	WHERE (rt.id = 3 OR al.id IN 
						(SELECT sh.login_id FROM kpi_service_history sh
						INNER JOIN kpi_services s ON s.id = sh.service_id
						INNER JOIN lib_libraries l ON l.id = s.library_id
						WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid))))
	AND
	(COALESCE(p_searchstring, '') = '' OR (al.first_name ILIKE '%' || p_searchstring || '%') OR (al.second_name ILIKE '%' || p_searchstring || '%') OR (al.last_name ILIKE '%' || p_searchstring || '%'));
END;
$$;


--
-- Name: web_kpi_history_by_service_get(integer, timestamp with time zone, timestamp with time zone, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_history_by_service_get(p_serviceid integer, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("LoginID" integer, "FirstName" text, "SecondName" text, "LastName" text, "FullName" text, "DateCreated" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
	SELECT
		sh.login_id AS "LoginID",
		al.first_name AS "FirstName",
		al.second_name AS "SecondName",
		al.last_name AS "LastName",
		fn_fio_get(al.first_name, al.last_name, al.second_name) AS "FullName",
		sh.date_created AS "DateCreated"
	FROM kpi_service_history sh
	INNER JOIN auth_logins al ON al.id = sh.login_id
	WHERE sh.service_id = p_serviceid AND (al.date_created::DATE) BETWEEN (p_datestart::DATE) AND (p_dateend::DATE)
	ORDER BY 
		CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN fn_fio_get(al.first_name, al.last_name, al.second_name) END ASC NULLS LAST,
		CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN fn_fio_get(al.first_name, al.last_name, al.second_name) END DESC NULLS FIRST,
		CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN sh.date_created END ASC NULLS LAST,
		CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN sh.date_created END DESC NULLS FIRST
	OFFSET p_rowsoffset
	LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_kpi_report_by_employee_get(integer, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_report_by_employee_get(p_loginid integer, p_datestart timestamp with time zone, p_dateend timestamp with time zone) RETURNS TABLE("Date" timestamp with time zone, "ServicesCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_datestart > p_dateend THEN
        RETURN;
    END IF;

    IF p_datestart = p_dateend THEN
        RETURN QUERY
        WITH hours AS (
            SELECT generate_series(0, 23) AS hour_value
        )
        SELECT 
            (p_datestart + INTERVAL '1 hour' * h.hour_value)::timestamp AT TIME ZONE 'UTC' AS "Date",
            COUNT(sh.service_id)::integer AS "ServicesCount"
        FROM hours h
        LEFT JOIN kpi_service_history sh
            ON sh.login_id = p_loginid
            AND EXTRACT(HOUR FROM sh.date_created) = h.hour_value
            AND sh.date_created::DATE = p_datestart::DATE
        GROUP BY h.hour_value
        ORDER BY "Date";
    ELSE
        IF (EXTRACT(MONTH FROM p_datestart) <> EXTRACT(MONTH FROM p_dateend)) 
           AND (EXTRACT(YEAR FROM p_datestart) = EXTRACT(YEAR FROM p_dateend)) 
        THEN
            RETURN QUERY
            WITH RECURSIVE dates AS (
                SELECT DATE_TRUNC('month', p_datestart)::DATE AS date_value
                UNION ALL
                SELECT (date_value + INTERVAL '1 month')::DATE
                FROM dates
                WHERE date_value < DATE_TRUNC('month', p_dateend)::DATE + INTERVAL '1 month'
            )
            SELECT 
                d.date_value::timestamp AT TIME ZONE 'UTC' AS "Date",
                COALESCE(e.employees_count, 0)::integer AS "ServicesCount"
            FROM dates d
            LEFT JOIN (
                SELECT 
                    COUNT(sh.login_id)::integer AS employees_count,
                    DATE_TRUNC('month', sh.date_created)::DATE AS date_value
                FROM kpi_service_history sh
                WHERE sh.login_id = p_loginid
                GROUP BY DATE_TRUNC('month', sh.date_created)::DATE
            ) e ON e.date_value = d.date_value
            WHERE d.date_value BETWEEN p_datestart::DATE AND p_dateend::DATE
            ORDER BY "Date";
        ELSIF (EXTRACT(YEAR FROM p_datestart) <> EXTRACT(YEAR FROM p_dateend)) THEN
            RETURN QUERY
            WITH RECURSIVE years AS (
                SELECT DATE_TRUNC('year', p_datestart)::DATE AS year_value
                UNION ALL
                SELECT (year_value + INTERVAL '1 year')::DATE
                FROM years
                WHERE year_value < DATE_TRUNC('year', p_dateend)::DATE
            )
            SELECT 
                y.year_value::timestamp AT TIME ZONE 'UTC' AS "Date",
                COALESCE(e.employees_count, 0)::integer AS "ServicesCount"
            FROM years y
            LEFT JOIN (
                SELECT 
                    COUNT(sh.login_id)::integer AS employees_count,
                    DATE_TRUNC('year', sh.date_created)::DATE AS year_value
                FROM kpi_service_history sh
                WHERE sh.login_id = p_loginid
                GROUP BY DATE_TRUNC('year', sh.date_created)::DATE
            ) e ON e.year_value = y.year_value
            WHERE y.year_value BETWEEN DATE_TRUNC('year', p_datestart)::DATE AND DATE_TRUNC('year', p_dateend)::DATE
            ORDER BY "Date";
        ELSIF (EXTRACT(MONTH FROM p_datestart) = EXTRACT(MONTH FROM p_dateend)) 
              AND (EXTRACT(YEAR FROM p_datestart) = EXTRACT(YEAR FROM p_dateend))
        THEN
            RETURN QUERY
            WITH RECURSIVE dates AS (
                SELECT p_datestart::DATE AS day_value
                UNION ALL
                SELECT (day_value + INTERVAL '1 day')::DATE
                FROM dates
                WHERE day_value < p_dateend::DATE
            )
            SELECT 
                d.day_value::timestamp AT TIME ZONE 'UTC' AS "Date",
                COALESCE(e.employees_count, 0)::integer AS "ServicesCount"
            FROM dates d
            LEFT JOIN (
                SELECT 
                    COUNT(sh.login_id)::integer AS employees_count,
                    sh.date_created::DATE AS date_value
                FROM kpi_service_history sh
                WHERE sh.login_id = p_loginid
                GROUP BY sh.date_created::DATE
            ) e ON e.date_value = d.day_value
            ORDER BY "Date";
        ELSE
            RETURN QUERY
            SELECT 
                p_datestart::timestamp AT TIME ZONE 'UTC' AS "Date",
                0::integer AS "ServicesCount"
            WHERE 1=0;
        END IF;
    END IF;
END;
$$;


--
-- Name: web_kpi_report_by_service_get(integer, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_report_by_service_get(p_serviceid integer, p_datestart timestamp with time zone, p_dateend timestamp with time zone) RETURNS TABLE("Date" timestamp with time zone, "ServicesCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_datestart > p_dateend THEN
        RETURN;
    END IF;

    IF p_datestart::DATE = p_dateend::DATE THEN
        RETURN QUERY
        SELECT 
            (p_datestart::DATE + make_interval(hours => h))::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(h_counts."ServicesCount", 0) AS "ServicesCount"
        FROM generate_series(0, 23) AS h
        LEFT JOIN (
            SELECT 
                EXTRACT(HOUR FROM sh.date_created)::int AS hour_val,
                COUNT(*)::integer AS "ServicesCount"
            FROM kpi_service_history sh
            WHERE sh.service_id = p_serviceid
              AND sh.date_created >= p_datestart
              AND sh.date_created < p_datestart::DATE + INTERVAL '1 day'
            GROUP BY EXTRACT(HOUR FROM sh.date_created)
        ) h_counts ON h_counts.hour_val = h
        ORDER BY "Date";
        RETURN;
    END IF;

    IF (p_dateend - p_datestart) > INTERVAL '1 year' THEN
        RETURN QUERY
        SELECT 
            gs.year_start::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(y_counts."ServicesCount", 0) AS "ServicesCount"
        FROM generate_series(
            DATE_TRUNC('year', p_datestart)::DATE,
            DATE_TRUNC('year', p_dateend)::DATE,
            '1 year'::INTERVAL
        ) AS gs(year_start)
        LEFT JOIN (
            SELECT 
                DATE_TRUNC('year', sh.date_created)::DATE AS year_start,
                COUNT(*)::integer AS "ServicesCount"
            FROM kpi_service_history sh
            WHERE sh.service_id = p_serviceid
              AND sh.date_created >= p_datestart
              AND sh.date_created <= p_dateend
            GROUP BY DATE_TRUNC('year', sh.date_created)
        ) y_counts ON y_counts.year_start = gs.year_start
        ORDER BY "Date";
        RETURN;
    END IF;

    IF (p_dateend - p_datestart) > INTERVAL '1 month' THEN
        RETURN QUERY
        SELECT 
            gs.month_start::timestamp AT TIME ZONE 'UTC' AS "Date",
            COALESCE(m_counts."ServicesCount", 0) AS "ServicesCount"
        FROM generate_series(
            DATE_TRUNC('month', p_datestart)::DATE,
            DATE_TRUNC('month', p_dateend)::DATE,
            '1 month'::INTERVAL
        ) AS gs(month_start)
        LEFT JOIN (
            SELECT 
                DATE_TRUNC('month', sh.date_created)::DATE AS month_start,
                COUNT(*)::integer AS "ServicesCount"
            FROM kpi_service_history sh
            WHERE sh.service_id = p_serviceid
              AND sh.date_created >= p_datestart
              AND sh.date_created <= p_dateend
            GROUP BY DATE_TRUNC('month', sh.date_created)
        ) m_counts ON m_counts.month_start = gs.month_start
        ORDER BY "Date";
        RETURN;
    END IF;

    RETURN QUERY
    SELECT 
        gs.day_date::timestamp AT TIME ZONE 'UTC' AS "Date",
        COALESCE(d_counts."ServicesCount", 0) AS "ServicesCount"
    FROM generate_series(
        p_datestart::DATE,
        p_dateend::DATE,
        '1 day'::INTERVAL
    ) AS gs(day_date)
    LEFT JOIN (
        SELECT 
            sh.date_created::DATE AS day_date,
            COUNT(*)::integer AS "ServicesCount"
        FROM kpi_service_history sh
        WHERE sh.service_id = p_serviceid
          AND sh.date_created >= p_datestart
          AND sh.date_created <= p_dateend
        GROUP BY sh.date_created::DATE
    ) d_counts ON d_counts.day_date = gs.day_date
    ORDER BY "Date";
END;
$$;


--
-- Name: web_kpi_report_services_get(uuid, uuid, timestamp with time zone, timestamp with time zone, text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_report_services_get(p_libraryid uuid, p_ownerid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer) RETURNS TABLE("ServiceID" integer, "ServiceName" text, "LibraryID" uuid, "AppliedCount" bigint, "ReadersCount" bigint)
    LANGUAGE plpgsql
    AS $$
BEGIN
    p_datestart := p_datestart::DATE;
    p_dateend := p_dateend::DATE;

    RETURN QUERY
    WITH eids AS (
        SELECT al.id 
        FROM auth_logins al
        INNER JOIN auth_login_roles lr ON lr.login_id = al.id
        INNER JOIN auth_role_types rt ON rt.id = lr.role_type_id
        WHERE rt.id = 3 
        OR 
        al.id IN (SELECT sh.login_id FROM kpi_service_history sh
                INNER JOIN kpi_services s ON s.id = sh.service_id
                INNER JOIN lib_libraries l ON l.id = s.library_id
                WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid)))
    ),
    sids AS (
        SELECT s.id FROM kpi_services s
        INNER JOIN lib_libraries l ON l.id = s.library_id
        WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid))
    )
    SELECT
        s.id AS "ServiceID",
        s.name AS "ServiceName",
        s.library_id AS "LibraryID",
        COUNT(sh.id) AS "AppliedCount",
        (SELECT COUNT(DISTINCT (sh2.reader_id)) FROM kpi_service_history sh2 WHERE sh2.service_id = s.id AND sh2.date_created BETWEEN p_datestart AND p_dateend) AS "ReadersCount"
    FROM kpi_services s
    INNER JOIN kpi_service_history sh ON sh.service_id = s.id
    INNER JOIN lib_libraries l ON s.library_id = l.id
    WHERE
    sh.date_created BETWEEN p_datestart AND p_dateend
    AND 
    ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid)) 
    AND
    (COALESCE(p_searchstring, '') = '' OR (s.name LIKE '%' || p_searchstring || '%'))
    GROUP BY s.id, s.name, s.library_id
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN s.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN s.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN COUNT(sh.id) END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN COUNT(sh.id) END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN (SELECT COUNT(DISTINCT (sh3.reader_id)) FROM kpi_service_history sh3 WHERE sh3.service_id = s.id AND sh3.date_created BETWEEN p_datestart AND p_dateend) END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN (SELECT COUNT(DISTINCT (sh4.reader_id)) FROM kpi_service_history sh4 WHERE sh4.service_id = s.id AND sh4.date_created BETWEEN p_datestart AND p_dateend) END DESC NULLS LAST;
END;
$$;


--
-- Name: web_kpi_service_modify(integer, text, integer, boolean, boolean, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_service_modify(p_id integer, p_name text, p_typeid integer, p_isauthrequired boolean, p_isdeleted boolean, p_libraryid uuid, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
	IF p_id IS NULL THEN
		INSERT INTO kpi_services (name, type_id, is_auth_required, library_id)
		VALUES (p_name, p_typeid, p_isauthrequired, p_libraryid)
		RETURNING id INTO new_id;
	ELSE
		UPDATE kpi_services
		SET name = p_name,
			type_id = p_typeid,
			is_auth_required = p_isauthrequired,
			is_deleted = p_isdeleted,
			library_id = p_libraryid
		WHERE id = p_id;
		
		new_id := p_id;
	END IF;
END;
$$;


--
-- Name: web_kpi_services_count_by_employee_get(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_services_count_by_employee_get(p_loginid integer) RETURNS TABLE("TodayCount" integer, "LastWeekCount" integer, "LastMonthCount" integer, "CurrentYearCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    last_monday DATE;
    last_sunday DATE;
    start_of_current_month TIMESTAMP;
BEGIN
    last_monday := (DATE_TRUNC('week', CURRENT_DATE - INTERVAL '1 day') + INTERVAL '2 days')::DATE;
    last_sunday := (last_monday + INTERVAL '6 days')::DATE;
    start_of_current_month := DATE_TRUNC('month', CURRENT_TIMESTAMP);

    RETURN QUERY
    SELECT 
        (SELECT COUNT(id)::INTEGER FROM kpi_service_history WHERE login_id = p_loginid AND date_created::DATE = CURRENT_DATE) AS "TodayCount",
        (SELECT COUNT(id)::INTEGER FROM kpi_service_history WHERE login_id = p_loginid AND date_created::DATE BETWEEN last_monday AND last_sunday) AS "LastWeekCount",
        (SELECT COUNT(id)::INTEGER FROM kpi_service_history WHERE login_id = p_loginid AND date_created >= (start_of_current_month - INTERVAL '1 month') AND date_created < start_of_current_month) AS "LastMonthCount",
        (SELECT COUNT(id)::INTEGER FROM kpi_service_history WHERE login_id = p_loginid AND EXTRACT(YEAR FROM date_created) = EXTRACT(YEAR FROM CURRENT_DATE)) AS "CurrentYearCount";
END;
$$;


--
-- Name: web_kpi_services_get(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_services_get(p_library_id uuid, p_search_string text, p_owner_id uuid) RETURNS TABLE("ServiceID" integer, "ServiceName" text, "IsAuthRequired" boolean, "TypeID" integer, "TypeName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        s.id::INTEGER AS "ServiceID",          
        s.name AS "ServiceName",
        s.is_auth_required AS "IsAuthRequired",
        st.id::INTEGER AS "TypeID",            
        st.name AS "TypeName"
    FROM kpi_services s
    INNER JOIN lib_libraries l ON l.id = s.library_id
    LEFT JOIN kpi_services_types st ON st.id = s.type_id
    WHERE ((p_library_id IS NOT NULL AND l.id = p_library_id) 
           OR (p_library_id IS NULL AND l.owner_id = p_owner_id))
      AND (COALESCE(p_search_string, '') = '' OR s.name LIKE '%' || p_search_string || '%')
      AND s.is_deleted = false
    ORDER BY s.name;
END;
$$;


--
-- Name: web_kpi_services_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_kpi_services_types_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        st.id::int AS "ID",
        st.name::text AS "Name"
    FROM kpi_servicestypes st;
END;
$$;


--
-- Name: web_lib_books_get(uuid, uuid, text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_lib_books_get(p_ownerid uuid, p_libraryid uuid, p_searchstring text, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("BookID" integer, "BookName" text, "BookCode" text, "AuthorsName" text, "BookPlace" text, "IsSpecial" boolean, "Description" text, "BookRate" numeric, "BookRateCount" bigint, "BookCheckOutCount" bigint, "BookCheckInCount" bigint, "BookProlongCount" bigint, "LibraryID" uuid, "RowsCount" bigint)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_searchstring IS NULL THEN
        p_searchstring := '';
    END IF;

    RETURN QUERY
    SELECT
        b.id AS "BookID",
        b.book_name AS "BookName",
        b.book_code AS "BookCode",
        b.authors_name AS "AuthorsName",
		b.book_place AS "BookPlace",
        b.is_special AS "IsSpecial",
        b.description AS "Description",
        (SELECT AVG(rate) FROM lib_book_user_ratings WHERE book_id = b.id)::numeric AS "BookRate",
        (SELECT COUNT(id) FROM lib_book_user_ratings WHERE book_id = b.id)::bigint AS "BookRateCount",
        (SELECT COUNT(id) FROM mon_stations_event_history WHERE message = b.book_code AND event_id = 10)::bigint AS "BookCheckOutCount",
        (SELECT COUNT(id) FROM mon_stations_event_history WHERE message = b.book_code AND event_id = 11)::bigint AS "BookCheckInCount",
        (SELECT COUNT(id) FROM mon_stations_event_history WHERE message = b.book_code AND event_id = 13)::bigint AS "BookProlongCount",
        b.library_id AS "LibraryID",
        COUNT(1) OVER () AS "RowsCount"
    FROM lib_books b
    LEFT JOIN mon_book_shelves_stat bs ON bs.book_id = b.id
    LEFT JOIN lib_books_covers bc ON b.id = bc.book_id
    INNER JOIN lib_libraries l ON l.id = b.library_id
    WHERE 
        (p_searchstring = '' OR (
            b.authors_name ILIKE '%' || p_searchstring || '%' OR
            b.book_name ILIKE '%' || p_searchstring || '%' OR
            b.book_code ILIKE '%' || p_searchstring || '%' OR
            b.book_place ILIKE '%' || p_searchstring || '%'
        ))
        AND
        ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid))
        AND 
        b.is_book = 1
    ORDER BY b.book_name
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_lib_reader_modify(integer, integer, text, text, text, text, text, integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_lib_reader_modify(p_op_id integer, p_reader_id integer, p_name text, p_abis_code text, p_rfid_code text, p_serial_number text, p_email text, INOUT p_new_id integer, p_owner_id uuid DEFAULT NULL::uuid) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_op_id = 1 THEN
        p_new_id := -1;
        
        IF NOT EXISTS (
            SELECT 1 FROM rdr_readers 
            WHERE owner_id = p_owner_id 
            AND (abis_code = p_abis_code 
                OR rfid_code = p_rfid_code 
                OR abis_code = p_rfid_code
                OR rfid_code = p_abis_code)
        ) THEN
            INSERT INTO rdr_readers (name, abis_code, rfid_code, serial_number, email, owner_id)
            VALUES (p_name, p_abis_code, p_rfid_code, p_serial_number, p_email, p_owner_id)
            RETURNING id INTO p_new_id;
        END IF;
        
    ELSIF p_op_id = 2 THEN
        UPDATE rdr_readers
        SET rfid_code = COALESCE(p_rfid_code, rfid_code),
            abis_code = COALESCE(p_abis_code, abis_code),
            serial_number = COALESCE(p_serial_number, serial_number),
            email = COALESCE(p_email, email)
        WHERE id = p_reader_id;
        
        p_new_id := p_reader_id;
    END IF;
END;
$$;


--
-- Name: web_lib_readers_get(uuid, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_lib_readers_get(p_ownerid uuid, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "DateCreated" timestamp with time zone, "Name" text, "AbisCode" text, "RFIDCode" text, "SerialNumber" text, "Email" text, "IsHavePhoto" boolean, "IsHaveCard" boolean, "RowsCount" bigint)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        r.id AS "ID",
        r.date_created AS "DateCreated",
        r.name AS "Name",
        r.abis_code AS "AbisCode",
        r.rfid_code AS "RFIDCode",
        r.serial_number AS "SerialNumber",
        r.email AS "Email",
        EXISTS (
            SELECT 1 FROM rdr_reader_photos 
            WHERE is_deleted = false AND reader_id = r.id
        ) AS "IsHavePhoto",
        (r.rfid_code IS NOT NULL) AS "IsHaveCard",
        COUNT(1) OVER () AS "RowsCount"
    FROM rdr_readers r
    WHERE 
        r.owner_id = p_ownerid
        AND (
            COALESCE(p_searchstring, '') = ''
            OR r.name        ILIKE '%' || p_searchstring || '%'
            OR r.abis_code   ILIKE '%' || p_searchstring || '%'
        )
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN r.name END ASC        NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN r.name END DESC       NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN r.abis_code END ASC   NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN r.abis_code END DESC  NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN (r.rfid_code IS NOT NULL) END ASC  NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN (r.rfid_code IS NOT NULL) END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN (
            EXISTS(SELECT 1 FROM rdr_reader_photos 
                   WHERE is_deleted = false AND reader_id = r.id)
        ) END ASC  NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN (
            EXISTS(SELECT 1 FROM rdr_reader_photos 
                   WHERE is_deleted = false AND reader_id = r.id)
        ) END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_libraries_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_libraries_get(p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "Fullname" text, "Manager" text, "Phone" text, "Address" text, "TagServiceCount" integer, "TagServiceCountHash" text, "TS" integer, "DateCreated" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        l.id::uuid,
        l.name::text,
        l.fullname::text,      
        l.manager::text,
        l.phone::text,
        l.address::text,
        l.tag_service_count::integer,
        l.tag_service_count_hash::text,
        l.ts::integer,
        l.date_created::timestamptz
    FROM lib_libraries l
    WHERE l.owner_id = p_ownerid
    ORDER BY l.name;
END;
$$;


--
-- Name: web_login_access_get(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_login_access_get(p_loginid integer) RETURNS TABLE("ID" integer, "ModuleName" text, "IsAllowed" boolean)
    LANGUAGE plpgsql
    AS $$
DECLARE
    r RECORD;
BEGIN
    CREATE TEMP TABLE temp_table (
        id INTEGER,
        module_name TEXT,
        is_allowed BOOLEAN
    ) ON COMMIT DROP;

    -- Инициализируем все модули с is_allowed = false
    INSERT INTO temp_table (id, module_name, is_allowed)
    SELECT id, name, false
    FROM main_modules;

    -- Пытаемся найти настройки для login_id
    IF EXISTS (SELECT 1 FROM auth_login_roles_ext WHERE login_id = p_loginid) THEN
        -- Получаем одну запись (PK гарантирует единственность)
        SELECT * INTO r FROM auth_login_roles_ext WHERE login_id = p_loginid;

        -- Обновляем только известные модули
        UPDATE temp_table SET is_allowed = r.is_view_monitoring      WHERE id = 1;
        UPDATE temp_table SET is_allowed = r.is_view_eas              WHERE id = 2;
        UPDATE temp_table SET is_allowed = r.is_view_safe_keepers     WHERE id = 3;
        UPDATE temp_table SET is_allowed = r.is_view_stations         WHERE id = 4;
        UPDATE temp_table SET is_allowed = r.is_view_smart_shelves    WHERE id = 5;
        UPDATE temp_table SET is_allowed = r.is_view_desktop_readers  WHERE id = 6;
        UPDATE temp_table SET is_allowed = r.is_view_acs              WHERE id = 7;
        UPDATE temp_table SET is_allowed = r.is_view_librarian        WHERE id = 8;
        UPDATE temp_table SET is_allowed = r.is_view_book_keeping     WHERE id = 9;
        UPDATE temp_table SET is_allowed = r.is_view_reports          WHERE id = 10;
        UPDATE temp_table SET is_allowed = r.is_view_face_detect      WHERE id = 11;
        UPDATE temp_table SET is_allowed = r.is_view_users            WHERE id = 12;
        UPDATE temp_table SET is_allowed = r.is_view_settings         WHERE id = 13;
        UPDATE temp_table SET is_allowed = r.is_view_kpi_analytic     WHERE id = 14;
        UPDATE temp_table SET is_allowed = r.is_view_vendings         WHERE id = 15;
        UPDATE temp_table SET is_allowed = r.is_view_smart_assistent  WHERE id = 16;
        UPDATE temp_table SET is_allowed = r.is_view_libraries        WHERE id = 17;

        -- Остальные модули (если есть с id > 17) останутся false — безопасно
    ELSE
        -- Если настроек нет — разрешаем всё
        UPDATE temp_table SET is_allowed = true;
    END IF;

    RETURN QUERY
    SELECT 
        t.id AS "ID",
        t.module_name AS "ModuleName", 
        t.is_allowed AS "IsAllowed"
    FROM temp_table t
    ORDER BY t.module_name;
END;
$$;


--
-- Name: web_login_access_modify(integer, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_login_access_modify(p_loginid integer, p_isviewmonitoring boolean, p_isvieweas boolean, p_isviewsafekeepers boolean, p_isviewstations boolean, p_isviewsmartshelves boolean, p_isviewdesktopreaders boolean, p_isviewacs boolean, p_isviewlibrarian boolean, p_isviewbookkeeping boolean, p_isviewreports boolean, p_isviewkpi_analytic boolean, p_isviewfacedetect boolean, p_isviewusers boolean, p_isviewsettings boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM auth_login_roles_ext WHERE login_id = p_loginid) THEN
        INSERT INTO auth_login_roles_ext(
            login_id,
            is_view_monitoring,
            is_view_eas,
            is_view_safe_keepers,
            is_view_stations,
            is_view_smart_shelves,
            is_view_desktop_readers,
            is_view_acs,
            is_view_librarian,
            is_view_book_keeping,
            is_view_reports,
            is_view_kpi_analytic,
            is_view_face_detect,
            is_view_users,
            is_view_settings)
        VALUES(
            p_loginid,
            p_isviewmonitoring,
            p_isvieweas,
            p_isviewsafekeepers,
            p_isviewstations,
            p_isviewsmartshelves,
            p_isviewdesktopreaders,
            p_isviewacs,
            p_isviewlibrarian,
            p_isviewbookkeeping,
            p_isviewreports,
            p_isviewkpi_analytic,
            p_isviewfacedetect,
            p_isviewusers,
            p_isviewsettings);
    ELSE
        UPDATE auth_login_roles_ext
            SET is_view_monitoring = p_isviewmonitoring,
                is_view_eas = p_isvieweas,
                is_view_safe_keepers = p_isviewsafekeepers,
                is_view_stations = p_isviewstations,
                is_view_smart_shelves = p_isviewsmartshelves,
                is_view_desktop_readers = p_isviewdesktopreaders,
                is_view_acs = p_isviewacs,
                is_view_librarian = p_isviewlibrarian,
                is_view_book_keeping = p_isviewbookkeeping,
                is_view_reports = p_isviewreports,
                is_view_kpi_analytic = p_isviewkpi_analytic,
                is_view_face_detect = p_isviewfacedetect,
                is_view_users = p_isviewusers,
                is_view_settings = p_isviewsettings
            WHERE login_id = p_loginid;
    END IF;
END;
$$;


--
-- Name: web_login_info_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_login_info_get(p_loginid integer, p_ownerid uuid) RETURNS TABLE("ID" integer, "DateCreated" timestamp with time zone, "Login" text, "FirstName" text, "SecondName" text, "LastName" text, "Expired" timestamp with time zone, "OwnerID" uuid, "OwnerName" text, "OwnerAddress" text, "Version" integer, "TS1" text, "RoleId" integer, "RoleName" text, "IsUseEKP" boolean, "IsInternalAccessEnabled" boolean, "DateBP" timestamp with time zone, "InternalDate" timestamp with time zone, "PaymentDate" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        a.id::integer AS "ID",
        a.date_created AS "DateCreated",  -- уже timestamptz — не трогаем
        a.login AS "Login",
        a.first_name AS "FirstName",
        a.second_name AS "SecondName", 
        a.last_name AS "LastName",
        a.expired AS "Expired",  -- уже timestamptz
        a.owner_id AS "OwnerID",
        o.name AS "OwnerName",
        o.address AS "OwnerAddress",  
        o.version AS "Version",
        o.ts1 AS "TS1",             
        r.id AS "RoleId",
        r.name AS "RoleName",
        o.is_use_ekp AS "IsUseEKP",
		a.is_internal_access_enabled AS "IsInternalAccessEnabled",
        CASE 
            WHEN current_setting('server_version_num')::INTEGER = 0 
            THEN CURRENT_TIMESTAMP
            ELSE i.date_bp
        END AS "DateBP",
        CURRENT_TIMESTAMP AS "InternalDate",
        (COALESCE(i.payment_date, CURRENT_DATE) + INTERVAL '1 day') AS "PaymentDate"
    FROM auth_logins a
    INNER JOIN auth_login_roles l ON a.id = l.login_id
    INNER JOIN auth_role_types r ON r.id = l.role_type_id
    INNER JOIN owners o ON o.id = a.owner_id
    INNER JOIN internal i ON i.id = o.internal_id
    WHERE a.id = p_loginid AND a.is_deleted = false AND o.id = p_ownerid;
END;
$$;


--
-- Name: web_login_roles_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_login_roles_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        rt.id::int AS "ID",
        rt.name::text AS "Name"
    FROM auth_roletype rt;
END;
$$;


--
-- Name: web_logins_get(uuid, text, integer, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_logins_get(p_ownerid uuid, p_searchstring text, p_sortid integer, p_sortdirection integer, p_filterid integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "Login" text, "FirstName" text, "SecondName" text, "LastName" text, "Expired" timestamp with time zone, "DateCreated" timestamp with time zone, "IsDeleted" boolean, "RoleTypeId" integer, "RoleTypeName" text, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    -- Приводим NULL к пустой строке, чтобы избежать пустого результата при поиске
    IF p_searchstring IS NULL THEN
        p_searchstring := '';
    END IF;

    RETURN QUERY
    SELECT 
        a.id AS "ID",
        a.login AS "Login",
        a.first_name AS "FirstName",
        a.second_name AS "SecondName",
        a.last_name AS "LastName",
        a.expired AS "Expired",
        a.date_created AS "DateCreated",
        a.is_deleted AS "IsDeleted",
        r.id AS "RoleTypeId",
        r.name AS "RoleTypeName",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM auth_logins a
    INNER JOIN auth_login_roles lr ON a.id = lr.login_id
    INNER JOIN auth_role_types r ON lr.role_type_id = r.id
    WHERE a.owner_id = p_ownerid
        AND a.is_deleted IN (
            CASE WHEN p_filterid = 0 THEN false ELSE true END,
            false
        )
        AND (p_searchstring = '' OR (
            (a.last_name ILIKE '%' || p_searchstring || '%') OR
            (a.first_name ILIKE '%' || p_searchstring || '%') OR
            (a.second_name ILIKE '%' || p_searchstring || '%') OR
            (a.login ILIKE '%' || p_searchstring || '%')
        ))
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN a.last_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN a.last_name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN a.first_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN a.first_name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN a.second_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN a.second_name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN a.login END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN a.login END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN r.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN r.name END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_owners_get(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_owners_get(p_login_id integer) RETURNS TABLE("ID" uuid, "Name" text, "DateCreated" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_internal_id uuid;  
BEGIN
    SELECT o.internal_id
    INTO v_internal_id
    FROM owners o
    INNER JOIN auth_logins al ON o.id = al.owner_id
    WHERE al.id = p_login_id
    LIMIT 1;

    RETURN QUERY
    SELECT 
        o.id AS "ID",
        o.name AS "Name",
        o.date_created AS "DateCreated"
    FROM owners o
    WHERE o.internal_id = v_internal_id  
    ORDER BY o.name;
END;
$$;


--
-- Name: web_reader_photo_modify(integer, bytea); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_reader_photo_modify(p_readerid integer, p_photo bytea) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    BEGIN
        UPDATE rdr_reader_photos SET photo = null, is_deleted = true
        WHERE reader_id = p_readerid;
        
        IF p_photo IS NOT NULL THEN
            INSERT INTO rdr_reader_photos(reader_id, photo)
            VALUES(p_readerid, p_photo);
        END IF;
        
        EXCEPTION WHEN OTHERS THEN
            RAISE;
    END;
END;
$$;


--
-- Name: web_reader_rfid_code_set(integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_reader_rfid_code_set(p_readerid integer, p_rfidcode text) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    UPDATE rdr_readers
    SET rfid_code = p_rfidcode
    WHERE id = p_readerid;
END;
$$;


--
-- Name: web_readers_stats_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_readers_stats_get(p_ownerid uuid) RETURNS TABLE("ReadersCount" integer, "ReadersWithPhotoCount" integer, "ReadersWithRFIDCardCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        COUNT(r.id)::INTEGER AS "ReadersCount",
        (SELECT COUNT(id)::INTEGER FROM rdr_reader_photos WHERE reader_id IN (SELECT id FROM rdr_readers WHERE owner_id = p_ownerid)) AS "ReadersWithPhotoCount",
        SUM(CASE WHEN (r.rfid_code IS NULL OR r.rfid_code = '') THEN 0 ELSE 1 END)::INTEGER AS "ReadersWithRFIDCardCount"
    FROM rdr_readers r
    WHERE r.owner_id = p_ownerid;
END;
$$;


--
-- Name: web_report_dashboard_get(uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_dashboard_get(p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("EASCount" integer, "StationsCount" integer, "DesktopDevicesCount" integer, "SafeKeepersCount" integer, "SmartShelvesCount" integer, "ACSCount" integer, "OverallCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
    
    eas_count integer := 0;
    stations_count integer := 0;
    desktop_devices integer := 0;
    safe_keepers integer := 0;
    smart_shelves integer := 0;
    acs_count integer := 0;
BEGIN
    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг валидности пользователя
    is_valid := CASE WHEN user_owner_id IS NOT NULL THEN true ELSE false END;

    -- CTE для фильтрации библиотек по правам доступа
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    )
    SELECT 
        COUNT(DISTINCT g.id),
        COUNT(DISTINCT CASE WHEN s.station_type_id NOT IN (3, 4) THEN s.id END),
        COUNT(DISTINCT d.id),
        COUNT(DISTINCT CASE WHEN s.station_type_id = 4 THEN s.id END),
        COUNT(DISTINCT CASE WHEN s.station_type_id = 3 THEN s.id END),
        COUNT(DISTINCT ae.id)
    INTO 
        eas_count,
        stations_count,
        desktop_devices,
        safe_keepers,
        smart_shelves,
        acs_count
    FROM allowed_libraries al
    LEFT JOIN mon_gates g ON g.library_id = al.library_id
    LEFT JOIN mon_stations s ON s.library_id = al.library_id
    LEFT JOIN mon_devices d ON d.library_id = al.library_id
    LEFT JOIN acs_entrances ae ON ae.library_id = al.library_id;

    -- ЕДИНСТВЕННЫЙ SELECT → стабильные метаданные
    RETURN QUERY
    SELECT 
        COALESCE(eas_count, 0)::integer AS "EASCount",
        COALESCE(stations_count, 0)::integer AS "StationsCount",
        COALESCE(desktop_devices, 0)::integer AS "DesktopDevicesCount",
        COALESCE(safe_keepers, 0)::integer AS "SafeKeepersCount",
        COALESCE(smart_shelves, 0)::integer AS "SmartShelvesCount",
        COALESCE(acs_count, 0)::integer AS "ACSCount",
        COALESCE(eas_count + stations_count + desktop_devices + safe_keepers + smart_shelves + acs_count, 0)::integer AS "OverallCount";
END;
$$;


--
-- Name: web_report_desktop_readers_advanced_get(uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_desktop_readers_advanced_get(p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("ID" uuid, "Name" text, "OperationsToday" integer, "OperationsByLastWeek" integer, "OperationsByCurrentMonth" integer, "OperationsByCurrentYear" integer, "OperationsOverall" integer, "LastOperation" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
DECLARE
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
    
    today_date date;
    week_start date;
    month_start date;
    year_start date;
BEGIN
    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг валидности пользователя
    is_valid := CASE WHEN user_owner_id IS NOT NULL THEN true ELSE false END;

    -- Статичные диапазоны дат
    today_date := CURRENT_DATE;
    week_start := today_date - INTERVAL '7 days';
    month_start := DATE_TRUNC('month', today_date)::date;
    year_start := DATE_TRUNC('year', today_date)::date;

    RETURN QUERY
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    device_stats AS (
        SELECT 
            d.id AS device_id,
            d.name AS device_name,
            al.library_name,
            al.owner_name,
            -- Операции за сегодня
            SUM(CASE WHEN mdd.date_value = today_date THEN mdd.device_ok_count ELSE 0 END) AS operations_today,
            -- Операции за последнюю неделю
            SUM(CASE WHEN mdd.date_value >= week_start THEN mdd.device_ok_count ELSE 0 END) AS operations_by_last_week,
            -- Операции за текущий месяц
            SUM(CASE WHEN mdd.date_value >= month_start THEN mdd.device_ok_count ELSE 0 END) AS operations_by_current_month,
            -- Операции за текущий год
            SUM(CASE WHEN mdd.date_value >= year_start THEN mdd.device_ok_count ELSE 0 END) AS operations_by_current_year,
            -- Все операции
            SUM(mdd.device_ok_count) AS operations_overall,
            -- Последняя операция
            MAX(mdd.date_value) AS last_operation
        FROM mon_devices d
        INNER JOIN allowed_libraries al ON d.library_id = al.library_id
        LEFT JOIN mon_device_data mdd ON mdd.device_id = d.id
        GROUP BY d.id, d.name, al.library_name, al.owner_name
    )
    SELECT 
        device_id AS "ID",
        CASE 
            WHEN p_owner_id IS NULL AND is_internal_access = true 
            THEN (owner_name || ' / ' || library_name || ' / ' || device_name)
            ELSE (library_name || ' / ' || device_name)
        END::text AS "Name",
        COALESCE(operations_today, 0)::integer AS "OperationsToday",
        COALESCE(operations_by_last_week, 0)::integer AS "OperationsByLastWeek",
        COALESCE(operations_by_current_month, 0)::integer AS "OperationsByCurrentMonth",
        COALESCE(operations_by_current_year, 0)::integer AS "OperationsByCurrentYear",
        COALESCE(operations_overall, 0)::integer AS "OperationsOverall",
        COALESCE(last_operation, '1900-01-01'::date) AS "LastOperation"
    FROM device_stats
    ORDER BY "Name";
END;
$$;


--
-- Name: web_report_desktop_readers_get(timestamp without time zone, timestamp without time zone, uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_desktop_readers_get(p_date_from timestamp without time zone, p_date_to timestamp without time zone, p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("OperationsPerDay" integer, "OperationsByPeriod" integer, "MaxAVGOperationsPerDay" integer, "MinAVGOperationsPerDay" integer, "MostActiveDevice" text, "LeastActiveDevice" text, "LastDevice" text, "LastDeviceID" uuid, "LastDeviceLibraryID" uuid, "LastOperationDate" date)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_from date;
    date_to date;
    days_count integer;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
BEGIN
    -- Приведение дат (исправлено: корректное количество дней)
    date_from := p_date_from::date;
    date_to := p_date_to::date;
    days_count := (date_to - date_from) + 1;  -- ← ИСПРАВЛЕНО

    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг: разрешено ли выполнять запрос?
    is_valid := CASE 
        WHEN days_count <= 0 OR user_owner_id IS NULL THEN false 
        ELSE true 
    END;

    RETURN QUERY
    WITH filtered_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.id AS owner_id,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    filtered_devices AS (
        SELECT 
            d.id AS device_id,
            d.name AS device_name,
            fl.library_id,
            fl.library_name,
            fl.owner_id,
            fl.owner_name,
            CONCAT(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true THEN fl.owner_name || ' / ' ELSE '' END,
                fl.library_name || ' / ',
                d.name
            ) AS full_device_path
        FROM mon_devices d
        INNER JOIN filtered_libraries fl ON d.library_id = fl.library_id
    ),
    operations_data AS (
        SELECT 
            dd.device_id,
            dd.device_ok_count,
            dd.date_value
        FROM mon_device_data dd
        INNER JOIN filtered_devices fd ON dd.device_id = fd.device_id
        WHERE 
            is_valid = true
            AND dd.date_value >= date_from 
            AND dd.date_value <= date_to  -- ← ИСПРАВЛЕНО: убран +1 day
    ),
    operation_stats AS (
        SELECT 
            device_id, 
            SUM(device_ok_count) AS total_operations
        FROM operations_data
        GROUP BY device_id
    ),
    most_active_device AS (
        SELECT device_id 
        FROM operation_stats 
        ORDER BY total_operations DESC
        LIMIT 1
    ),
    least_active_device AS (
        SELECT device_id 
        FROM operation_stats 
        ORDER BY total_operations ASC
        LIMIT 1
    ),
    last_operation_device AS (
        SELECT device_id 
        FROM operations_data 
        ORDER BY date_value DESC
        LIMIT 1
    ),
    total_operations AS (
        SELECT 
            SUM(total_operations) AS total,
            MAX(total_operations) AS max_total,
            MIN(total_operations) AS min_total
        FROM operation_stats
    )

    -- ЕДИНСТВЕННЫЙ SELECT с тем же набором столбцов
    SELECT 
        COALESCE((total / NULLIF(days_count, 0))::integer, 0) AS "OperationsPerDay",
        COALESCE(total, 0)::integer AS "OperationsByPeriod",
        COALESCE((max_total / NULLIF(days_count, 0))::integer, 0) AS "MaxAVGOperationsPerDay",
        COALESCE((min_total / NULLIF(days_count, 0))::integer, 0) AS "MinAVGOperationsPerDay",
        COALESCE((SELECT fd.full_device_path FROM filtered_devices fd INNER JOIN most_active_device mad ON fd.device_id = mad.device_id), '-')::text AS "MostActiveDevice",
        COALESCE((SELECT fd.full_device_path FROM filtered_devices fd INNER JOIN least_active_device lad ON fd.device_id = lad.device_id), '-')::text AS "LeastActiveDevice",
        COALESCE((SELECT fd.full_device_path FROM filtered_devices fd INNER JOIN last_operation_device lod ON fd.device_id = lod.device_id), '-')::text AS "LastDevice",
        (SELECT device_id FROM last_operation_device) AS "LastDeviceID",
        (SELECT fd.library_id FROM filtered_devices fd INNER JOIN last_operation_device lod ON fd.device_id = lod.device_id) AS "LastDeviceLibraryID",
        (SELECT MAX(date_value)::date FROM operations_data) AS "LastOperationDate"  -- Приведение к типу date
    FROM total_operations;
END;
$$;


--
-- Name: web_report_gates_advanced_get(uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_gates_advanced_get(p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("ID" uuid, "Name" text, "AlarmsToday" integer, "AlarmsByLastWeek" integer, "AlarmsByCurrentMonth" integer, "AlarmsByCurrentYear" integer, "AlarmsOverall" integer, "LastAlarm" timestamp with time zone, "LastBook" text, "IsAlive" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    today_date date;
    tomorrow_date date;
    start_of_month date;
    start_of_year date;
    start_of_week date;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
BEGIN
    -- Даты
    today_date := CURRENT_DATE;
    tomorrow_date := today_date + INTERVAL '1 day';
    start_of_month := DATE_TRUNC('month', CURRENT_DATE)::date;
    start_of_year := DATE_TRUNC('year', CURRENT_DATE)::date;
    start_of_week := DATE_TRUNC('week', CURRENT_DATE)::date;

    -- Данные пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг валидности пользователя
    is_valid := CASE WHEN user_owner_id IS NOT NULL THEN true ELSE false END;

    RETURN QUERY
       WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    gate_base AS (
        SELECT 
            g.id AS gate_id,
            g.name AS gate_name,
            al.library_id,
            al.library_name,
            al.owner_name,
            g.last_update
        FROM mon_gates g
        INNER JOIN allowed_libraries al ON g.library_id = al.library_id
    ),
    alarms_enriched AS (
        SELECT 
            ge.gate_id,
            ge.uid,
            ge.date_value,
            CASE WHEN ge.date_value::date = today_date THEN 1 ELSE 0 END AS is_today,
            CASE WHEN ge.date_value >= start_of_week AND ge.date_value < tomorrow_date THEN 1 ELSE 0 END AS is_current_week,
            CASE WHEN ge.date_value >= start_of_month THEN 1 ELSE 0 END AS is_current_month,
            CASE WHEN ge.date_value >= start_of_year THEN 1 ELSE 0 END AS is_current_year
        FROM mon_gates_eas ge
        INNER JOIN gate_base gb ON ge.gate_id = gb.gate_id
    ),
    aggregated_alarms AS (
        SELECT 
            gate_id,
            SUM(is_today) AS alarms_today,
            SUM(is_current_week) AS alarms_by_last_week,
            SUM(is_current_month) AS alarms_by_current_month,
            SUM(is_current_year) AS alarms_by_current_year,
            COUNT(*) AS alarms_overall,
            MAX(date_value) AS last_alarm_date,
            (SELECT uid FROM alarms_enriched ae2 WHERE ae2.gate_id = ae1.gate_id ORDER BY date_value DESC LIMIT 1) AS last_uid
        FROM alarms_enriched ae1
        GROUP BY gate_id
    ),
    last_book_info AS (
        SELECT 
            aa.gate_id,
            COALESCE(b.book_name, 'Неизвестная книга') AS last_book
        FROM aggregated_alarms aa
        LEFT JOIN lib_books b ON b.book_code = aa.last_uid
    )
    SELECT 
        gb.gate_id AS "ID",
        CASE 
            WHEN p_owner_id IS NULL AND is_internal_access = true 
            THEN (gb.owner_name || ' / ' || gb.library_name || ' / ' || gb.gate_name)
            ELSE (gb.library_name || ' / ' || gb.gate_name)
        END::text AS "Name",
        COALESCE(aa.alarms_today, 0)::integer AS "AlarmsToday",
        COALESCE(aa.alarms_by_last_week, 0)::integer AS "AlarmsByLastWeek",
        COALESCE(aa.alarms_by_current_month, 0)::integer AS "AlarmsByCurrentMonth",
        COALESCE(aa.alarms_by_current_year, 0)::integer AS "AlarmsByCurrentYear",
        COALESCE(aa.alarms_overall, 0)::integer AS "AlarmsOverall",
        aa.last_alarm_date AS "LastAlarm",
        COALESCE(lb.last_book, 'Неизвестная книга')::text AS "LastBook",
        CASE WHEN EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - gb.last_update)) / 60 <= 5 THEN 1 ELSE 0 END::integer AS "IsAlive"
    FROM gate_base gb
    LEFT JOIN aggregated_alarms aa ON gb.gate_id = aa.gate_id
    LEFT JOIN last_book_info lb ON gb.gate_id = lb.gate_id
    ORDER BY 
        CASE WHEN p_owner_id IS NULL AND is_internal_access = true THEN gb.owner_name ELSE '' END,
        gb.library_name, 
        gb.gate_name;
END;
$$;


--
-- Name: web_report_gates_get(timestamp without time zone, timestamp without time zone, uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_gates_get(p_date_from timestamp without time zone, p_date_to timestamp without time zone, p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("AlarmsPerDay" integer, "AlarmsByPeriod" integer, "MaxAVGAlarmsPerDay" integer, "MinAVGAlarmsPerDay" integer, "BooksByPeriod" integer, "MostActiveSystem" text, "LeastActiveSystem" text, "LastSystem" text, "LastSystemID" uuid, "LastSystemLibraryID" uuid, "LastAlarmDate" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_from date;
    date_to date;
    days_count integer;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
BEGIN
    -- Приведение дат
    date_from := p_date_from::date;
    date_to   := p_date_to::date;
    days_count := (date_to - date_from) + 1;

    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг: разрешено ли выполнять запрос?
    is_valid := CASE 
        WHEN days_count <= 0 OR user_owner_id IS NULL THEN false 
        ELSE true 
    END;

    RETURN QUERY
    WITH filtered_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.id AS owner_id,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    filtered_gates AS (
        SELECT 
            g.id AS gate_id,
            g.name AS gate_name,
            fl.library_id,
            fl.library_name,
            fl.owner_id,
            fl.owner_name,
            CONCAT(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true THEN fl.owner_name || ' / ' ELSE '' END,
                fl.library_name || ' / ',
                g.name
            ) AS full_gate_path
        FROM mon_gates g
        INNER JOIN filtered_libraries fl ON g.library_id = fl.library_id
    ),
    alarms_data AS (
        SELECT 
            ge.gate_id,
            ge.uid,
            ge.date_value
        FROM mon_gates_eas ge
        INNER JOIN filtered_gates fg ON ge.gate_id = fg.gate_id
        WHERE 
            is_valid = true
            AND ge.date_value >= date_from 
            AND ge.date_value < date_to + INTERVAL '1 day'  
    ),
    alarm_stats AS (
        SELECT gate_id, COUNT(*) AS alarm_count
        FROM alarms_data
        GROUP BY gate_id
    ),
    most_active_gate AS (
        SELECT gate_id 
        FROM alarm_stats 
        ORDER BY alarm_count DESC
        LIMIT 1
    ),
    least_active_gate AS (
        SELECT gate_id 
        FROM alarm_stats 
        ORDER BY alarm_count ASC
        LIMIT 1
    ),
    last_alarm_gate AS (
        SELECT gate_id 
        FROM alarms_data 
        ORDER BY date_value DESC
        LIMIT 1
    ),
    aggregated_stats AS (
        SELECT 
            COALESCE(SUM(alarm_count), 0) AS total_alarms,
            COALESCE(MAX(alarm_count), 0) AS max_alarms,
            COALESCE(MIN(alarm_count), 0) AS min_alarms,
            COALESCE((SELECT COUNT(DISTINCT uid) FROM alarms_data), 0) AS unique_books
        FROM alarm_stats
    )
    SELECT 
        COALESCE((ag.total_alarms / NULLIF(days_count, 0))::integer, 0) AS "AlarmsPerDay",
        COALESCE(ag.total_alarms, 0)::integer AS "AlarmsByPeriod",
        COALESCE((ag.max_alarms / NULLIF(days_count, 0))::integer, 0) AS "MaxAVGAlarmsPerDay",
        COALESCE((ag.min_alarms / NULLIF(days_count, 0))::integer, 0) AS "MinAVGAlarmsPerDay",
        COALESCE(ag.unique_books, 0)::integer AS "BooksByPeriod",
        COALESCE((SELECT fg.full_gate_path FROM filtered_gates fg INNER JOIN most_active_gate mag ON fg.gate_id = mag.gate_id), '-')::text AS "MostActiveSystem",
        COALESCE((SELECT fg.full_gate_path FROM filtered_gates fg INNER JOIN least_active_gate lag ON fg.gate_id = lag.gate_id), '-')::text AS "LeastActiveSystem",
        COALESCE((SELECT fg.full_gate_path FROM filtered_gates fg INNER JOIN last_alarm_gate lag ON fg.gate_id = lag.gate_id), '-')::text AS "LastSystem",
        (SELECT gate_id FROM last_alarm_gate) AS "LastSystemID",
        (SELECT fg.library_id FROM filtered_gates fg INNER JOIN last_alarm_gate lag ON fg.gate_id = lag.gate_id) AS "LastSystemLibraryID",
        (SELECT MAX(ge.date_value) FROM alarms_data ge) AS "LastAlarmDate"
    FROM aggregated_stats ag;
END;
$$;


--
-- Name: web_report_safe_keepers_advanced_get(uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_safe_keepers_advanced_get(p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("ID" uuid, "Name" text, "KeepingsToday" integer, "OrdersToday" integer, "VendingsToday" integer, "KeepingsByLastWeek" integer, "OrdersByLastWeek" integer, "VendingsByLastWeek" integer, "KeepingsByCurrentMonth" integer, "OrdersByCurrentMonth" integer, "VendingsByCurrentMonth" integer, "KeepingsByCurrentYear" integer, "OrdersByCurrentYear" integer, "VendingsByCurrentYear" integer, "KeepingsOverall" integer, "OrdersOverall" integer, "VendingsOverall" integer, "LastOperationDate" timestamp with time zone, "LastOperationEventID" integer, "IsAlive" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
    
    today_date date;
    week_start date;
    month_start date;
    year_start date;
BEGIN
    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг валидности пользователя
    is_valid := CASE WHEN user_owner_id IS NOT NULL THEN true ELSE false END;

    -- Статичные диапазоны дат
    today_date := CURRENT_DATE;
    week_start := today_date - INTERVAL '7 days';
    month_start := DATE_TRUNC('month', CURRENT_DATE)::date;
    year_start := DATE_TRUNC('year', CURRENT_DATE)::date;

    RETURN QUERY
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    station_base AS (
        SELECT 
            s.id AS station_id,
            s.name AS station_name,
            s.last_update,
            al.library_name,
            al.owner_name
        FROM mon_stations s
        INNER JOIN allowed_libraries al ON s.library_id = al.library_id
        WHERE s.station_type_id IN (4, 9) -- SafeKeepers stations only
    ),
    history_stats AS (
        SELECT 
            sh.safe_keeper_id AS station_id,
            -- Сегодня
            SUM(CASE WHEN sh.date_created::date = today_date AND sh.event_type_id = 1 THEN 1 ELSE 0 END) AS keepings_today,
            SUM(CASE WHEN sh.date_created::date = today_date AND sh.event_type_id = 3 THEN 1 ELSE 0 END) AS orders_today,
            SUM(CASE WHEN sh.date_created::date = today_date AND sh.event_type_id = 8 THEN 1 ELSE 0 END) AS vendings_today,
            -- Последняя неделя
            SUM(CASE WHEN sh.date_created >= week_start AND sh.event_type_id = 1 THEN 1 ELSE 0 END) AS keepings_by_last_week,
            SUM(CASE WHEN sh.date_created >= week_start AND sh.event_type_id = 3 THEN 1 ELSE 0 END) AS orders_by_last_week,
            SUM(CASE WHEN sh.date_created >= week_start AND sh.event_type_id = 8 THEN 1 ELSE 0 END) AS vendings_by_last_week,
            -- Текущий месяц
            SUM(CASE WHEN sh.date_created >= month_start AND sh.event_type_id = 1 THEN 1 ELSE 0 END) AS keepings_by_current_month,
            SUM(CASE WHEN sh.date_created >= month_start AND sh.event_type_id = 3 THEN 1 ELSE 0 END) AS orders_by_current_month,
            SUM(CASE WHEN sh.date_created >= month_start AND sh.event_type_id = 8 THEN 1 ELSE 0 END) AS vendings_by_current_month,
            -- Текущий год
            SUM(CASE WHEN sh.date_created >= year_start AND sh.event_type_id = 1 THEN 1 ELSE 0 END) AS keepings_by_current_year,
            SUM(CASE WHEN sh.date_created >= year_start AND sh.event_type_id = 3 THEN 1 ELSE 0 END) AS orders_by_current_year,
            SUM(CASE WHEN sh.date_created >= year_start AND sh.event_type_id = 8 THEN 1 ELSE 0 END) AS vendings_by_current_year,
            -- Все время
            SUM(CASE WHEN sh.event_type_id = 1 THEN 1 ELSE 0 END) AS keepings_overall,
            SUM(CASE WHEN sh.event_type_id = 3 THEN 1 ELSE 0 END) AS orders_overall,
            SUM(CASE WHEN sh.event_type_id = 8 THEN 1 ELSE 0 END) AS vendings_overall,
            -- Последняя операция
            MAX(sh.date_created) AS last_operation_date
        FROM sk_history sh
        INNER JOIN station_base sb ON sh.safe_keeper_id = sb.station_id
        WHERE sh.event_type_id IN (1, 3, 8)
        GROUP BY sh.safe_keeper_id
    ),
    last_events AS (
        SELECT DISTINCT ON (sh.safe_keeper_id)
            sh.safe_keeper_id AS station_id,
            sh.event_type_id AS last_operation_event_id
        FROM sk_history sh
        WHERE sh.event_type_id IN (1, 3, 8)
        ORDER BY sh.safe_keeper_id, sh.date_created DESC
    )
    SELECT 
        sb.station_id AS "ID",
        CASE 
            WHEN p_owner_id IS NULL AND is_internal_access = true 
            THEN (sb.owner_name || ' / ' || sb.library_name || ' / ' || sb.station_name)
            ELSE (sb.library_name || ' / ' || sb.station_name)
        END::text AS "Name",
        COALESCE(hs.keepings_today, 0)::integer AS "KeepingsToday",
        COALESCE(hs.orders_today, 0)::integer AS "OrdersToday",
        COALESCE(hs.vendings_today, 0)::integer AS "VendingsToday",
        COALESCE(hs.keepings_by_last_week, 0)::integer AS "KeepingsByLastWeek",
        COALESCE(hs.orders_by_last_week, 0)::integer AS "OrdersByLastWeek",
        COALESCE(hs.vendings_by_last_week, 0)::integer AS "VendingsByLastWeek",
        COALESCE(hs.keepings_by_current_month, 0)::integer AS "KeepingsByCurrentMonth",
        COALESCE(hs.orders_by_current_month, 0)::integer AS "OrdersByCurrentMonth",
        COALESCE(hs.vendings_by_current_month, 0)::integer AS "VendingsByCurrentMonth",
        COALESCE(hs.keepings_by_current_year, 0)::integer AS "KeepingsByCurrentYear",
        COALESCE(hs.orders_by_current_year, 0)::integer AS "OrdersByCurrentYear",
        COALESCE(hs.vendings_by_current_year, 0)::integer AS "VendingsByCurrentYear",
        COALESCE(hs.keepings_overall, 0)::integer AS "KeepingsOverall",
        COALESCE(hs.orders_overall, 0)::integer AS "OrdersOverall",
        COALESCE(hs.vendings_overall, 0)::integer AS "VendingsOverall",
        hs.last_operation_date AS "LastOperationDate",
        COALESCE(le.last_operation_event_id, 0)::integer AS "LastOperationEventID",
        CASE WHEN EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - sb.last_update)) / 60 <= 5 THEN 1 ELSE 0 END::integer AS "IsAlive"
    FROM station_base sb
    LEFT JOIN history_stats hs ON sb.station_id = hs.station_id
    LEFT JOIN last_events le ON sb.station_id = le.station_id
    ORDER BY 
        CASE WHEN p_owner_id IS NULL AND is_internal_access = true THEN sb.owner_name ELSE '' END,
        sb.library_name, 
        sb.station_name;
END;
$$;


--
-- Name: web_report_safe_keepers_get(timestamp without time zone, timestamp without time zone, uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_safe_keepers_get(p_date_from timestamp without time zone, p_date_to timestamp without time zone, p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("PutThingsPerDay" integer, "PutThingsByPeriod" integer, "MaxAVGPutThingsPerDay" integer, "MinAVGPutThingsPerDay" integer, "MostActiveStation" text, "LeastActiveStation" text, "LastStation" text, "LastStationID" uuid, "LastStationLibraryID" uuid, "LastPutThingsDate" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_from date;
    date_to date;
    days_count integer;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
BEGIN
    -- Приведение дат (исправлено)
    date_from := p_date_from::date;
    date_to := p_date_to::date;
    days_count := (date_to - date_from) + 1;  -- ← ИСПРАВЛЕНО

    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг: разрешено ли выполнять запрос?
    is_valid := CASE 
        WHEN days_count <= 0 OR user_owner_id IS NULL THEN false 
        ELSE true 
    END;

    RETURN QUERY
    WITH filtered_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.id AS owner_id,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    filtered_stations AS (
        SELECT 
            s.id AS station_id,
            s.name AS station_name,
            fl.library_id,
            fl.library_name,
            fl.owner_id,
            fl.owner_name,
            CONCAT(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true THEN fl.owner_name || ' / ' ELSE '' END,
                fl.library_name || ' / ',
                s.name
            ) AS full_station_path
        FROM mon_stations s
        INNER JOIN filtered_libraries fl ON s.library_id = fl.library_id
    ),
    put_things_data AS (
        SELECT 
            h.safe_keeper_id AS station_id,
            h.date_created,
            h.event_type_id
        FROM sk_history h
        INNER JOIN filtered_stations fs ON h.safe_keeper_id = fs.station_id
        WHERE 
            is_valid = true
            AND h.date_created >= date_from 
            AND h.date_created < date_to + INTERVAL '1 day'  -- ← ИСПРАВЛЕНО: < вместо <=
            AND h.event_type_id = 1  -- PutThings events only
    ),
    put_things_stats AS (
        SELECT 
            station_id, 
            COUNT(*) AS put_things_count
        FROM put_things_data
        GROUP BY station_id
    ),
    most_active_station AS (
        SELECT station_id 
        FROM put_things_stats 
        ORDER BY put_things_count DESC
        LIMIT 1
    ),
    least_active_station AS (
        SELECT station_id 
        FROM put_things_stats 
        ORDER BY put_things_count ASC
        LIMIT 1
    ),
    last_put_things_station AS (
        SELECT station_id 
        FROM put_things_data 
        ORDER BY date_created DESC
        LIMIT 1
    ),
    total_put_things AS (
        SELECT 
            COALESCE(SUM(put_things_count), 0) AS total,
            COALESCE(MAX(put_things_count), 0) AS max_total,
            COALESCE(MIN(put_things_count), 0) AS min_total
        FROM put_things_stats
    )
    SELECT 
        COALESCE((tp.total / NULLIF(days_count, 0))::integer, 0) AS "PutThingsPerDay",
        COALESCE(tp.total, 0)::integer AS "PutThingsByPeriod",
        COALESCE((tp.max_total / NULLIF(days_count, 0))::integer, 0) AS "MaxAVGPutThingsPerDay",
        COALESCE((tp.min_total / NULLIF(days_count, 0))::integer, 0) AS "MinAVGPutThingsPerDay",
        COALESCE((SELECT fs.full_station_path FROM filtered_stations fs INNER JOIN most_active_station mas ON fs.station_id = mas.station_id), '-')::text AS "MostActiveStation",
        COALESCE((SELECT fs.full_station_path FROM filtered_stations fs INNER JOIN least_active_station las ON fs.station_id = las.station_id), '-')::text AS "LeastActiveStation",
        COALESCE((SELECT fs.full_station_path FROM filtered_stations fs INNER JOIN last_put_things_station lps ON fs.station_id = lps.station_id), '-')::text AS "LastStation",
        (SELECT station_id FROM last_put_things_station) AS "LastStationID",
        (SELECT fs.library_id FROM filtered_stations fs INNER JOIN last_put_things_station lps ON fs.station_id = lps.station_id) AS "LastStationLibraryID",
        (SELECT MAX(date_created) FROM put_things_data) AS "LastPutThingsDate"
    FROM total_put_things tp;
END;
$$;


--
-- Name: web_report_skud_advanced_get(uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_skud_advanced_get(p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("ID" integer, "Name" text, "PassesToday" integer, "UniquePassesToday" integer, "PassesByLastWeek" integer, "UniquePassesByLastWeek" integer, "PassesByCurrentMonth" integer, "UniquePassesByCurrentMonth" integer, "PassesByCurrentYear" integer, "UniquePassesByCurrentYear" integer, "PassesOverall" integer, "UniquePassesOverall" integer, "LastOperationDate" timestamp with time zone, "LastOperationEventID" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    
    today_date date;
    week_start date;
    month_start date;
    year_start date;
BEGIN
    -- Быстрая проверка пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    IF user_owner_id IS NULL THEN
        RETURN; -- Возвращаем пустой результат
    END IF;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Даты для фильтрации
    today_date := CURRENT_DATE;
    week_start := today_date - INTERVAL '7 days';
    month_start := DATE_TRUNC('month', CURRENT_DATE)::date;
    year_start := DATE_TRUNC('year', CURRENT_DATE)::date;

    RETURN QUERY
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE (
            -- Сценарий 1: Указана конкретная библиотека
            (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                CASE 
                    -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                    WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                    -- При is_internal_access = true и есть internal_id - проверяем internal группу
                    WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                    -- При is_internal_access = true и нет internal_id - проверяем владельца
                    WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                    ELSE false
                END
            )
            OR
            -- Сценарий 2: Указан конкретный владелец
            (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                CASE 
                    WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                    WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                        EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                    WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                    ELSE false
                END
            )
            OR
            -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
            (p_library_id IS NULL AND p_owner_id IS NULL AND
                CASE 
                    WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                    WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                    WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                    ELSE false
                END
            )
        )
    ),
    entrance_base AS (
        SELECT 
            e.id AS entrance_id,
            CASE 
                WHEN p_owner_id IS NULL AND is_internal_access = true 
                THEN (o.name || ' / ' || l.name || ' / ' || e.name)
                ELSE (l.name || ' / ' || e.name)
            END AS full_name
        FROM acs_entrances e
        INNER JOIN allowed_libraries al ON e.library_id = al.library_id
        INNER JOIN lib_libraries l ON e.library_id = l.id
        INNER JOIN owners o ON l.owner_id = o.id
    ),
    reader_events AS (
        SELECT 
            r.entrance_id,
            eh.event_date,
            eh.rfid_code,
            CASE WHEN eh.event_date::date = today_date THEN 1 ELSE 0 END AS is_today,
            CASE WHEN eh.event_date >= week_start THEN 1 ELSE 0 END AS is_last_week,
            CASE WHEN eh.event_date >= month_start THEN 1 ELSE 0 END AS is_current_month,
            CASE WHEN eh.event_date >= year_start THEN 1 ELSE 0 END AS is_current_year
        FROM acs_events_history eh
        INNER JOIN acs_readers r ON eh.reader_id = r.id
        INNER JOIN entrance_base eb ON r.entrance_id = eb.entrance_id
        WHERE eh.event_date >= year_start
    ),
    aggregated_stats AS (
        SELECT 
            entrance_id,
            -- Сегодня
            SUM(is_today) AS passes_today,
            COUNT(DISTINCT CASE WHEN is_today = 1 THEN rfid_code END) AS unique_passes_today,
            -- Неделя
            SUM(is_last_week) AS passes_by_last_week,
            COUNT(DISTINCT CASE WHEN is_last_week = 1 THEN rfid_code END) AS unique_passes_by_last_week,
            -- Месяц
            SUM(is_current_month) AS passes_by_current_month,
            COUNT(DISTINCT CASE WHEN is_current_month = 1 THEN rfid_code END) AS unique_passes_by_current_month,
            -- Год
            SUM(is_current_year) AS passes_by_current_year,
            COUNT(DISTINCT CASE WHEN is_current_year = 1 THEN rfid_code END) AS unique_passes_by_current_year,
            -- Все время
            COUNT(*) AS passes_overall,
            COUNT(DISTINCT rfid_code) AS unique_passes_overall,
            MAX(event_date) AS last_operation_date
        FROM reader_events
        GROUP BY entrance_id
    )
    SELECT 
        eb.entrance_id AS "ID",
        eb.full_name::text AS "Name",
        COALESCE(a.passes_today, 0)::integer AS "PassesToday",
        COALESCE(a.unique_passes_today, 0)::integer AS "UniquePassesToday",
        COALESCE(a.passes_by_last_week, 0)::integer AS "PassesByLastWeek",
        COALESCE(a.unique_passes_by_last_week, 0)::integer AS "UniquePassesByLastWeek",
        COALESCE(a.passes_by_current_month, 0)::integer AS "PassesByCurrentMonth",
        COALESCE(a.unique_passes_by_current_month, 0)::integer AS "UniquePassesByCurrentMonth",
        COALESCE(a.passes_by_current_year, 0)::integer AS "PassesByCurrentYear",
        COALESCE(a.unique_passes_by_current_year, 0)::integer AS "UniquePassesByCurrentYear",
        COALESCE(a.passes_overall, 0)::integer AS "PassesOverall",
        COALESCE(a.unique_passes_overall, 0)::integer AS "UniquePassesOverall",
        a.last_operation_date AS "LastOperationDate",
        1::integer AS "LastOperationEventID"
    FROM entrance_base eb
    LEFT JOIN aggregated_stats a ON eb.entrance_id = a.entrance_id
    ORDER BY eb.full_name;
END;
$$;


--
-- Name: web_report_skud_get(timestamp without time zone, timestamp without time zone, uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_skud_get(p_date_from timestamp without time zone, p_date_to timestamp without time zone, p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("PassesPerDay" integer, "PassesByPeriod" integer, "MaxAVGPassesPerDay" integer, "MinAVGPassesPerDay" integer, "ReadersByPeriod" integer, "MostActiveEntrance" text, "LeastActiveEntrance" text, "LastEntrance" text, "LastEntranceID" integer, "LastEntranceLibraryID" uuid, "LastPassDate" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_from date;
    date_to date;
    days_count integer;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
    
    total_passes bigint := 0;
    max_passes bigint := 0;
    min_passes bigint := 0;
    unique_readers integer := 0;
    most_active_entrance_id integer := NULL;
    least_active_entrance_id integer := NULL;
    last_entrance_id integer := NULL;
    last_pass_date timestamp with time zone := NULL;
    last_entrance_library_id uuid := NULL;
BEGIN
    -- Приведение дат (исправлено: включаем весь день)
    date_from := p_date_from::date;
    date_to   := p_date_to::date;
    days_count := (date_to - date_from) + 1;  -- ← ИСПРАВЛЕНО: +1

    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг: разрешено ли выполнять запрос?
    is_valid := CASE 
        WHEN days_count <= 0 OR user_owner_id IS NULL THEN false 
        ELSE true 
    END;

    IF is_valid THEN
        -- Собираем статистику по проходам
        WITH passes_stats AS (
            SELECT 
                e.id AS entrance_id,
                COUNT(*) AS passes_count
            FROM acs_events_history eh
            INNER JOIN acs_readers r ON r.id = eh.reader_id
            INNER JOIN acs_entrances e ON e.id = r.entrance_id
            INNER JOIN lib_libraries l ON l.id = e.library_id
            INNER JOIN owners o ON l.owner_id = o.id
            WHERE 
                eh.event_date >= date_from 
                AND eh.event_date < date_to + INTERVAL '1 day'  -- ← ИСПРАВЛЕНО
                AND (
                    -- Сценарий 1: Указана конкретная библиотека
                    (p_library_id IS NOT NULL AND e.library_id = p_library_id AND 
                        CASE 
                            -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                            WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                            -- При is_internal_access = true и есть internal_id - проверяем internal группу
                            WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                            -- При is_internal_access = true и нет internal_id - проверяем владельца
                            WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                            ELSE false
                        END
                    )
                    OR
                    -- Сценарий 2: Указан конкретный владелец
                    (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                        CASE 
                            WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                            WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                                EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                            WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                            ELSE false
                        END
                    )
                    OR
                    -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                    (p_library_id IS NULL AND p_owner_id IS NULL AND
                        CASE 
                            WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                            WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                            WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                            ELSE false
                        END
                    )
                )
            GROUP BY e.id
        )
        SELECT 
            COALESCE(SUM(passes_count), 0),
            COALESCE(MAX(passes_count), 0),
            COALESCE(MIN(passes_count), 0)
        INTO 
            total_passes,
            max_passes,
            min_passes
        FROM passes_stats;

        -- Самый активный вход
        SELECT e.id
        INTO most_active_entrance_id
        FROM acs_events_history eh
        INNER JOIN acs_readers r ON r.id = eh.reader_id
        INNER JOIN acs_entrances e ON e.id = r.entrance_id
        INNER JOIN lib_libraries l ON l.id = e.library_id
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            eh.event_date >= date_from 
            AND eh.event_date < date_to + INTERVAL '1 day'  -- ← ИСПРАВЛЕНО
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND e.library_id = p_library_id AND 
                    CASE 
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
        GROUP BY e.id
        ORDER BY COUNT(*) DESC
        LIMIT 1;

        -- Самый неактивный вход
        SELECT e.id
        INTO least_active_entrance_id
        FROM acs_events_history eh
        INNER JOIN acs_readers r ON r.id = eh.reader_id
        INNER JOIN acs_entrances e ON e.id = r.entrance_id
        INNER JOIN lib_libraries l ON l.id = e.library_id
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            eh.event_date >= date_from 
            AND eh.event_date < date_to + INTERVAL '1 day'  -- ← ИСПРАВЛЕНО
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND e.library_id = p_library_id AND 
                    CASE 
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
        GROUP BY e.id
        HAVING COUNT(*) > 0
        ORDER BY COUNT(*) ASC
        LIMIT 1;

        -- Последний вход
        SELECT 
            e.id,
            e.library_id,
            eh.event_date
        INTO 
            last_entrance_id,
            last_entrance_library_id,
            last_pass_date
        FROM acs_events_history eh
        INNER JOIN acs_readers r ON r.id = eh.reader_id
        INNER JOIN acs_entrances e ON e.id = r.entrance_id
        INNER JOIN lib_libraries l ON l.id = e.library_id
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND e.library_id = p_library_id AND 
                    CASE 
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
        ORDER BY eh.event_date DESC
        LIMIT 1;

        -- Уникальные читатели
        SELECT COUNT(DISTINCT eh.rfid_code)
        INTO unique_readers
        FROM acs_events_history eh
        INNER JOIN acs_readers r ON r.id = eh.reader_id
        INNER JOIN acs_entrances e ON e.id = r.entrance_id
        INNER JOIN lib_libraries l ON l.id = e.library_id
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            eh.event_date >= date_from 
            AND eh.event_date < date_to + INTERVAL '1 day'  -- ← ИСПРАВЛЕНО
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND e.library_id = p_library_id AND 
                    CASE 
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            );
    END IF;

    -- Финальный SELECT
    RETURN QUERY
    SELECT 
        CASE WHEN days_count > 0 THEN (total_passes / days_count)::integer ELSE 0 END AS "PassesPerDay",
        total_passes::integer AS "PassesByPeriod",
        CASE WHEN days_count > 0 THEN (max_passes / days_count)::integer ELSE 0 END AS "MaxAVGPassesPerDay",
        CASE WHEN days_count > 0 THEN (min_passes / days_count)::integer ELSE 0 END AS "MinAVGPassesPerDay",
        COALESCE(unique_readers, 0)::integer AS "ReadersByPeriod",
        COALESCE((
            SELECT 
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true THEN o.name || ' / ' ELSE '' END ||
                l.name || ' / ' ||
                e.name
            FROM acs_entrances e
            INNER JOIN lib_libraries l ON l.id = e.library_id
            INNER JOIN owners o ON l.owner_id = o.id
            WHERE e.id = most_active_entrance_id
        ), '-')::text AS "MostActiveEntrance",
        COALESCE((
            SELECT 
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true THEN o.name || ' / ' ELSE '' END ||
                l.name || ' / ' ||
                e.name
            FROM acs_entrances e
            INNER JOIN lib_libraries l ON l.id = e.library_id
            INNER JOIN owners o ON l.owner_id = o.id
            WHERE e.id = least_active_entrance_id
        ), '-')::text AS "LeastActiveEntrance",
        COALESCE((
            SELECT 
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true THEN o.name || ' / ' ELSE '' END ||
                l.name || ' / ' ||
                e.name
            FROM acs_entrances e
            INNER JOIN lib_libraries l ON l.id = e.library_id
            INNER JOIN owners o ON l.owner_id = o.id
            WHERE e.id = last_entrance_id
        ), '-')::text AS "LastEntrance",
        COALESCE(last_entrance_id, 0)::integer AS "LastEntranceID",
        last_entrance_library_id AS "LastEntranceLibraryID",
        last_pass_date AS "LastPassDate";
END;
$$;


--
-- Name: web_report_smart_shelves_advanced_get(uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_smart_shelves_advanced_get(p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("ID" uuid, "Name" text, "BooktakesToday" integer, "CheckinsToday" integer, "BooktakesByLastWeek" integer, "CheckinsByLastWeek" integer, "BooktakesByCurrentMonth" integer, "CheckinsByCurrentMonth" integer, "BooktakesByCurrentYear" integer, "CheckinsByCurrentYear" integer, "BooktakesOverall" integer, "CheckinsOverall" integer, "LastOperationDate" timestamp with time zone, "LastOperationEventID" integer, "LastBook" text, "IsAlive" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
    
    today_date date;
    week_start date;
    month_start date;
    year_start date;
BEGIN
    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг валидности пользователя
    is_valid := CASE WHEN user_owner_id IS NOT NULL THEN true ELSE false END;

    -- Статичные диапазоны дат
    today_date := CURRENT_DATE;
    week_start := today_date - INTERVAL '7 days';
    month_start := DATE_TRUNC('month', CURRENT_DATE)::date;
    year_start := DATE_TRUNC('year', CURRENT_DATE)::date;

    RETURN QUERY
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    station_base AS (
        SELECT 
            s.id AS station_id,
            s.name AS station_name,
            s.last_update,
            al.library_name,
            al.owner_name
        FROM mon_stations s
        INNER JOIN allowed_libraries al ON s.library_id = al.library_id
        WHERE s.station_type_id IN (3, 8) -- Smart Shelves stations only
    ),
    event_stats AS (
        SELECT 
            seh.station_id,
            -- Booktakes (EventID = 15)
            SUM(CASE WHEN seh.date_event::date = today_date AND seh.event_id = 15 THEN 1 ELSE 0 END) AS booktakes_today,
            SUM(CASE WHEN seh.date_event::date = today_date AND seh.event_id = 12 THEN 1 ELSE 0 END) AS checkins_today,
            -- Last Week
            SUM(CASE WHEN seh.date_event >= week_start AND seh.event_id = 15 THEN 1 ELSE 0 END) AS booktakes_by_last_week,
            SUM(CASE WHEN seh.date_event >= week_start AND seh.event_id = 12 THEN 1 ELSE 0 END) AS checkins_by_last_week,
            -- Current Month
            SUM(CASE WHEN seh.date_event >= month_start AND seh.event_id = 15 THEN 1 ELSE 0 END) AS booktakes_by_current_month,
            SUM(CASE WHEN seh.date_event >= month_start AND seh.event_id = 12 THEN 1 ELSE 0 END) AS checkins_by_current_month,
            -- Current Year
            SUM(CASE WHEN seh.date_event >= year_start AND seh.event_id = 15 THEN 1 ELSE 0 END) AS booktakes_by_current_year,
            SUM(CASE WHEN seh.date_event >= year_start AND seh.event_id = 12 THEN 1 ELSE 0 END) AS checkins_by_current_year,
            -- Overall
            SUM(CASE WHEN seh.event_id = 15 THEN 1 ELSE 0 END) AS booktakes_overall,
            SUM(CASE WHEN seh.event_id = 12 THEN 1 ELSE 0 END) AS checkins_overall,
            -- Last Operation
            MAX(seh.date_event) AS last_operation_date
        FROM mon_stations_event_history seh
        INNER JOIN station_base sb ON seh.station_id = sb.station_id
        WHERE seh.event_id IN (12, 15)
        GROUP BY seh.station_id
    ),
    last_events AS (
        SELECT DISTINCT ON (seh.station_id)
            seh.station_id,
            seh.event_id AS last_operation_event_id,
            seh.message AS last_book_code
        FROM mon_stations_event_history seh
        WHERE seh.event_id IN (12, 15)
        ORDER BY seh.station_id, seh.date_event DESC
    ),
    last_books AS (
        SELECT 
            le.station_id,
            COALESCE(b.book_name, 'Неизвестная книга') AS last_book
        FROM last_events le
        LEFT JOIN lib_books b ON b.book_code = le.last_book_code
    )
    SELECT 
        sb.station_id AS "ID",
        CASE 
            WHEN p_owner_id IS NULL AND is_internal_access = true 
            THEN (sb.owner_name || ' / ' || sb.library_name || ' / ' || sb.station_name)
            ELSE (sb.library_name || ' / ' || sb.station_name)
        END::text AS "Name",
        COALESCE(es.booktakes_today, 0)::integer AS "BooktakesToday",
        COALESCE(es.checkins_today, 0)::integer AS "CheckinsToday",
        COALESCE(es.booktakes_by_last_week, 0)::integer AS "BooktakesByLastWeek",
        COALESCE(es.checkins_by_last_week, 0)::integer AS "CheckinsByLastWeek",
        COALESCE(es.booktakes_by_current_month, 0)::integer AS "BooktakesByCurrentMonth",
        COALESCE(es.checkins_by_current_month, 0)::integer AS "CheckinsByCurrentMonth",
        COALESCE(es.booktakes_by_current_year, 0)::integer AS "BooktakesByCurrentYear",
        COALESCE(es.checkins_by_current_year, 0)::integer AS "CheckinsByCurrentYear",
        COALESCE(es.booktakes_overall, 0)::integer AS "BooktakesOverall",
        COALESCE(es.checkins_overall, 0)::integer AS "CheckinsOverall",
        es.last_operation_date AS "LastOperationDate",
        COALESCE(le.last_operation_event_id, 0)::integer AS "LastOperationEventID",
        COALESCE(lb.last_book, 'Неизвестная книга')::text AS "LastBook",
        CASE WHEN EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - sb.last_update)) / 60 <= 5 THEN 1 ELSE 0 END::integer AS "IsAlive"
    FROM station_base sb
    LEFT JOIN event_stats es ON sb.station_id = es.station_id
    LEFT JOIN last_events le ON sb.station_id = le.station_id
    LEFT JOIN last_books lb ON sb.station_id = lb.station_id
    ORDER BY 
        CASE WHEN p_owner_id IS NULL AND is_internal_access = true THEN sb.owner_name ELSE '' END,
        sb.library_name, 
        sb.station_name;
END;
$$;


--
-- Name: web_report_smart_shelves_get(timestamp without time zone, timestamp without time zone, uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_smart_shelves_get(p_date_from timestamp without time zone, p_date_to timestamp without time zone, p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("ReturnsPerDay" integer, "ReturnsByPeriod" integer, "MaxAVGReturnsPerDay" integer, "MinAVGReturnsPerDay" integer, "ReadersByPeriod" integer, "MostActiveStation" text, "LeastActiveStation" text, "LastStation" text, "LastStationID" uuid, "LastStationLibraryID" uuid, "LastReturnDate" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_from date;
    date_to date;
    days_count integer;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
BEGIN
    date_from := p_date_from::date;
    date_to := p_date_to::date;
    days_count := (date_to - date_from) + 1;  -- ← ИСПРАВЛЕНО: +1

    -- Проверка корректности периода и пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг валидности входных данных
    is_valid := CASE 
        WHEN days_count <= 0 OR user_owner_id IS NULL THEN false 
        ELSE true 
    END;

    RETURN QUERY
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.id AS owner_id,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    allowed_stations AS (
        SELECT 
            s.id AS station_id,
            s.name AS station_name,
            s.library_id,
            al.library_name,
            al.owner_name,
            al.library_id AS lib_id
        FROM mon_stations s
        INNER JOIN allowed_libraries al ON s.library_id = al.library_id
        WHERE s.station_type_id IN (3, 8) -- Smart Shelves stations only
    ),
    returns_filtered AS (
        SELECT 
            seh.station_id,
            seh.user_code,
            seh.date_event
        FROM mon_stations_event_history seh
        INNER JOIN allowed_stations ast ON seh.station_id = ast.station_id
        WHERE 
            is_valid = true
            AND seh.event_id = 12 -- Return events only
            AND seh.date_event >= date_from 
            AND seh.date_event < (date_to + INTERVAL '1 day')
    ),
    aggregated_returns AS (
        SELECT 
            station_id,
            COUNT(*) AS returns_count
        FROM returns_filtered
        GROUP BY station_id
    ),
    station_details AS (
        SELECT 
            ar.*,
            ast.station_name,
            ast.library_name,
            ast.owner_name,
            ast.library_id
        FROM aggregated_returns ar
        INNER JOIN allowed_stations ast ON ar.station_id = ast.station_id
    ),
    ranked_stations AS (
        SELECT *,
            ROW_NUMBER() OVER (ORDER BY returns_count DESC) AS rn_most,
            ROW_NUMBER() OVER (ORDER BY returns_count ASC) AS rn_least
        FROM station_details
    ),
    most_active AS (
        SELECT * FROM ranked_stations WHERE rn_most = 1
    ),
    least_active AS (
        SELECT * FROM ranked_stations WHERE rn_least = 1
    ),
    last_return_raw AS (
        SELECT 
            rf.station_id,
            rf.date_event
        FROM returns_filtered rf
        ORDER BY rf.date_event DESC
        LIMIT 1
    ),
    last_return AS (
        SELECT 
            lrr.station_id,
            lrr.date_event,
            ast.station_name,
            ast.library_name,
            ast.owner_name,
            ast.library_id
        FROM last_return_raw lrr
        INNER JOIN allowed_stations ast ON lrr.station_id = ast.station_id
    ),
    totals AS (
        SELECT 
            COALESCE(SUM(returns_count), 0) AS total_returns
        FROM aggregated_returns
    ),
    readers_count AS (
        SELECT COUNT(DISTINCT rf.user_code) AS readers_by_period
        FROM returns_filtered rf
    ),
    final_data AS (
        SELECT 
            COALESCE((SELECT total_returns / NULLIF(days_count, 0) FROM totals), 0)::integer AS returns_per_day,
            COALESCE((SELECT total_returns FROM totals), 0)::integer AS returns_by_period,
            COALESCE((SELECT returns_count / NULLIF(days_count, 0) FROM most_active), 0)::integer AS max_avg_returns_per_day,
            COALESCE((SELECT returns_count / NULLIF(days_count, 0) FROM least_active), 0)::integer AS min_avg_returns_per_day,
            COALESCE((SELECT readers_by_period FROM readers_count), 0)::integer AS readers_by_period,
            COALESCE(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true OR p_library_id IS NOT NULL 
                     THEN (SELECT owner_name || ' / ' || library_name || ' / ' || station_name FROM most_active)
                     ELSE (SELECT library_name || ' / ' || station_name FROM most_active)
                END, '-'
            ) AS most_active_station,
            COALESCE(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true OR p_library_id IS NOT NULL 
                     THEN (SELECT owner_name || ' / ' || library_name || ' / ' || station_name FROM least_active)
                     ELSE (SELECT library_name || ' / ' || station_name FROM least_active)
                END, '-'
            ) AS least_active_station,
            COALESCE(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true OR p_library_id IS NOT NULL 
                     THEN (SELECT owner_name || ' / ' || library_name || ' / ' || station_name FROM last_return)
                     ELSE (SELECT library_name || ' / ' || station_name FROM last_return)
                END, '-'
            ) AS last_station,
            (SELECT station_id FROM last_return) AS last_station_id,
            (SELECT library_id FROM last_return) AS last_station_library_id,
            (SELECT date_event FROM last_return) AS last_return_date
    )
    SELECT 
        returns_per_day AS "ReturnsPerDay",
        returns_by_period AS "ReturnsByPeriod",
        max_avg_returns_per_day AS "MaxAVGReturnsPerDay",
        min_avg_returns_per_day AS "MinAVGReturnsPerDay",
        readers_by_period AS "ReadersByPeriod",
        LEFT(most_active_station, 500)::text AS "MostActiveStation",
        LEFT(least_active_station, 500)::text AS "LeastActiveStation",
        LEFT(last_station, 500)::text AS "LastStation",
        last_station_id AS "LastStationID",
        last_station_library_id AS "LastStationLibraryID",
        last_return_date AS "LastReturnDate"
    FROM final_data;
END;
$$;


--
-- Name: web_report_stations_advanced_get(uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_stations_advanced_get(p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("ID" uuid, "Name" text, "CheckoutsToday" integer, "CheckinsToday" integer, "CheckoutsByLastWeek" integer, "CheckinsByLastWeek" integer, "CheckoutsByCurrentMonth" integer, "CheckinsByCurrentMonth" integer, "CheckoutsByCurrentYear" integer, "CheckinsByCurrentYear" integer, "CheckoutsOverall" integer, "CheckinsOverall" integer, "LastOperationDate" timestamp with time zone, "LastOperationEventID" integer, "LastBook" text, "IsAlive" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    today_date date;
    tomorrow_date date;
    week_start date;
    month_start date;
    year_start date;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
BEGIN
    -- Статичные диапазоны дат
    today_date := CURRENT_DATE;
    tomorrow_date := today_date + INTERVAL '1 day';
    week_start := today_date - INTERVAL '7 days';
    month_start := DATE_TRUNC('month', CURRENT_DATE)::date;
    year_start := DATE_TRUNC('year', CURRENT_DATE)::date;

    -- Проверка корректности пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    IF user_owner_id IS NULL THEN
        RETURN;
    END IF;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    RETURN QUERY
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE (
            -- Сценарий 1: Указана конкретная библиотека
            (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                CASE 
                    -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                    WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                    -- При is_internal_access = true и есть internal_id - проверяем internal группу
                    WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                    -- При is_internal_access = true и нет internal_id - проверяем владельца
                    WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                    ELSE false
                END
            )
            OR
            -- Сценарий 2: Указан конкретный владелец
            (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                CASE 
                    WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                    WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                        EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                    WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                    ELSE false
                END
            )
            OR
            -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
            (p_library_id IS NULL AND p_owner_id IS NULL AND
                CASE 
                    WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                    WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                    WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                    ELSE false
                END
            )
        )
    ),
    station_base AS (
        SELECT 
            s.id AS station_id,
            s.name AS station_name,
            s.last_update,
            al.library_name,
            al.owner_name
        FROM mon_stations s
        INNER JOIN allowed_libraries al ON s.library_id = al.library_id
        WHERE s.station_type_id NOT IN (3, 4, 9) -- Все станции кроме Smart Shelves, SafeKeepers
    ),
    event_stats AS (
        SELECT 
            seh.station_id,
            -- Сегодня
            SUM(CASE WHEN seh.date_event::date = today_date AND seh.event_id = 10 THEN 1 ELSE 0 END) AS checkouts_today,
            SUM(CASE WHEN seh.date_event::date = today_date AND seh.event_id = 11 THEN 1 ELSE 0 END) AS checkins_today,
            -- Последняя неделя
            SUM(CASE WHEN seh.date_event >= week_start AND seh.event_id = 10 THEN 1 ELSE 0 END) AS checkouts_by_last_week,
            SUM(CASE WHEN seh.date_event >= week_start AND seh.event_id = 11 THEN 1 ELSE 0 END) AS checkins_by_last_week,
            -- Текущий месяц
            SUM(CASE WHEN seh.date_event >= month_start AND seh.event_id = 10 THEN 1 ELSE 0 END) AS checkouts_by_current_month,
            SUM(CASE WHEN seh.date_event >= month_start AND seh.event_id = 11 THEN 1 ELSE 0 END) AS checkins_by_current_month,
            -- Текущий год
            SUM(CASE WHEN seh.date_event >= year_start AND seh.event_id = 10 THEN 1 ELSE 0 END) AS checkouts_by_current_year,
            SUM(CASE WHEN seh.date_event >= year_start AND seh.event_id = 11 THEN 1 ELSE 0 END) AS checkins_by_current_year,
            -- Все время
            SUM(CASE WHEN seh.event_id = 10 THEN 1 ELSE 0 END) AS checkouts_overall,
            SUM(CASE WHEN seh.event_id = 11 THEN 1 ELSE 0 END) AS checkins_overall,
            -- Последняя операция
            MAX(seh.date_event) AS last_operation_date
        FROM mon_stations_event_history seh
        INNER JOIN station_base sb ON seh.station_id = sb.station_id
        WHERE seh.event_id IN (10, 11)
        GROUP BY seh.station_id
    ),
    last_events AS (
        SELECT DISTINCT ON (seh.station_id)
            seh.station_id,
            seh.event_id AS last_operation_event_id,
            seh.message AS last_book_code
        FROM mon_stations_event_history seh
        WHERE seh.event_id IN (10, 11)
        ORDER BY seh.station_id, seh.date_event DESC
    ),
    last_books AS (
        SELECT 
            le.station_id,
            COALESCE(b.book_name, 'Неизвестная книга') AS last_book
        FROM last_events le
        LEFT JOIN lib_books b ON b.book_code = le.last_book_code
    )
    SELECT 
        sb.station_id AS "ID",
        CASE 
            WHEN p_owner_id IS NULL AND is_internal_access = true 
            THEN (sb.owner_name || ' / ' || sb.library_name || ' / ' || sb.station_name)
            ELSE (sb.library_name || ' / ' || sb.station_name)
        END::text AS "Name",
        COALESCE(es.checkouts_today, 0)::integer AS "CheckoutsToday",
        COALESCE(es.checkins_today, 0)::integer AS "CheckinsToday",
        COALESCE(es.checkouts_by_last_week, 0)::integer AS "CheckoutsByLastWeek",
        COALESCE(es.checkins_by_last_week, 0)::integer AS "CheckinsByLastWeek",
        COALESCE(es.checkouts_by_current_month, 0)::integer AS "CheckoutsByCurrentMonth",
        COALESCE(es.checkins_by_current_month, 0)::integer AS "CheckinsByCurrentMonth",
        COALESCE(es.checkouts_by_current_year, 0)::integer AS "CheckoutsByCurrentYear",
        COALESCE(es.checkins_by_current_year, 0)::integer AS "CheckinsByCurrentYear",
        COALESCE(es.checkouts_overall, 0)::integer AS "CheckoutsOverall",
        COALESCE(es.checkins_overall, 0)::integer AS "CheckinsOverall",
        es.last_operation_date AS "LastOperationDate",
        COALESCE(le.last_operation_event_id, 0)::integer AS "LastOperationEventID",
        COALESCE(lb.last_book, 'Неизвестная книга')::text AS "LastBook",
        CASE WHEN EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - sb.last_update)) / 60 <= 5 THEN 1 ELSE 0 END::integer AS "IsAlive"
    FROM station_base sb
    LEFT JOIN event_stats es ON sb.station_id = es.station_id
    LEFT JOIN last_events le ON sb.station_id = le.station_id
    LEFT JOIN last_books lb ON sb.station_id = lb.station_id
    ORDER BY 
        CASE WHEN p_owner_id IS NULL AND is_internal_access = true THEN sb.owner_name ELSE '' END,
        sb.library_name, 
        sb.station_name;
END;
$$;


--
-- Name: web_report_stations_get(timestamp without time zone, timestamp without time zone, uuid, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_report_stations_get(p_date_from timestamp without time zone, p_date_to timestamp without time zone, p_library_id uuid, p_owner_id uuid, p_login_id integer) RETURNS TABLE("CheckinsPerDay" integer, "CheckoutsByPeriod" integer, "CheckinsByPeriod" integer, "MaxAVGCheckinsPerDay" integer, "MinAVGCheckinsPerDay" integer, "ReadersByPeriod" integer, "MostActiveStation" text, "LeastActiveStation" text, "LastStation" text, "LastStationID" uuid, "LastStationLibraryID" uuid, "LastCheckinDate" timestamp with time zone)
    LANGUAGE plpgsql
    AS $$
DECLARE
    date_from date;
    date_to date;
    days_count integer;
    
    user_owner_id uuid;
    is_internal_access boolean := false;
    user_internal_id uuid := NULL;
    is_valid boolean := false;
BEGIN
    date_from := p_date_from::date;
    date_to := p_date_to::date;
    days_count := (date_to - date_from) + 1;  -- ← ИСПРАВЛЕНО: +1

    -- Проверка корректности периода и пользователя
    SELECT 
        owner_id,
        is_internal_access_enabled
    INTO 
        user_owner_id,
        is_internal_access
    FROM auth_logins
    WHERE id = p_login_id;

    SELECT internal_id 
    INTO user_internal_id
    FROM owners
    WHERE id = user_owner_id;

    -- Флаг валидности входных данных
    is_valid := CASE 
        WHEN days_count <= 0 OR user_owner_id IS NULL THEN false 
        ELSE true 
    END;

    RETURN QUERY
    WITH allowed_libraries AS (
        SELECT 
            l.id AS library_id,
            l.name AS library_name,
            o.id AS owner_id,
            o.name AS owner_name
        FROM lib_libraries l
        INNER JOIN owners o ON l.owner_id = o.id
        WHERE 
            is_valid = true
            AND (
                -- Сценарий 1: Указана конкретная библиотека
                (p_library_id IS NOT NULL AND l.id = p_library_id AND 
                    CASE 
                        -- При is_internal_access = false проверяем, что библиотека принадлежит пользователю
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        -- При is_internal_access = true и есть internal_id - проверяем internal группу
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        -- При is_internal_access = true и нет internal_id - проверяем владельца
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 2: Указан конкретный владелец
                (p_library_id IS NULL AND p_owner_id IS NOT NULL AND l.owner_id = p_owner_id AND
                    CASE 
                        WHEN is_internal_access = false THEN p_owner_id = user_owner_id
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN 
                            EXISTS (SELECT 1 FROM owners o2 WHERE o2.id = p_owner_id AND o2.internal_id = user_internal_id)
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN p_owner_id = user_owner_id
                        ELSE false
                    END
                )
                OR
                -- Сценарий 3: Ничего не указано - определяем доступные библиотеки
                (p_library_id IS NULL AND p_owner_id IS NULL AND
                    CASE 
                        WHEN is_internal_access = true AND user_internal_id IS NOT NULL THEN o.internal_id = user_internal_id
                        WHEN is_internal_access = true AND user_internal_id IS NULL THEN l.owner_id = user_owner_id
                        WHEN is_internal_access = false THEN l.owner_id = user_owner_id
                        ELSE false
                    END
                )
            )
    ),
    allowed_stations AS (
        SELECT 
            s.id AS station_id,
            s.name AS station_name,
            s.library_id,
            s.station_type_id,
            al.library_name,
            al.owner_name,
            al.library_id AS lib_id
        FROM mon_stations s
        INNER JOIN allowed_libraries al ON s.library_id = al.library_id
        WHERE s.station_type_id <> 4
    ),
    events_filtered AS (
        SELECT 
            seh.station_id,
            seh.event_id,
            seh.user_code,
            seh.date_event
        FROM mon_stations_event_history seh
        INNER JOIN allowed_stations ast ON seh.station_id = ast.station_id
        WHERE 
            is_valid = true
            AND seh.event_id IN (10, 11)
            AND seh.date_event >= date_from 
            AND seh.date_event < (date_to + INTERVAL '1 day')
    ),
    aggregated_events AS (
        SELECT 
            station_id,
            COUNT(CASE WHEN event_id = 10 THEN 1 END) AS checkouts,
            COUNT(CASE WHEN event_id = 11 THEN 1 END) AS checkins,
            COUNT(*) AS total_operations
        FROM events_filtered
        GROUP BY station_id
    ),
    station_details AS (
        SELECT 
            ae.*,
            ast.station_name,
            ast.library_name,
            ast.owner_name,
            ast.library_id
        FROM aggregated_events ae
        INNER JOIN allowed_stations ast ON ae.station_id = ast.station_id
    ),
    ranked_stations AS (
        SELECT *,
            ROW_NUMBER() OVER (ORDER BY total_operations DESC) AS rn_most,
            ROW_NUMBER() OVER (ORDER BY total_operations ASC) AS rn_least
        FROM station_details
    ),
    most_active AS (
        SELECT * FROM ranked_stations WHERE rn_most = 1
    ),
    least_active AS (
        SELECT * FROM ranked_stations WHERE rn_least = 1
    ),
    last_event_raw AS (
        SELECT 
            ef.station_id,
            ef.date_event
        FROM events_filtered ef
        ORDER BY ef.date_event DESC
        LIMIT 1
    ),
    last_event AS (
        SELECT 
            ler.station_id,
            ler.date_event,
            ast.station_name,
            ast.library_name,
            ast.owner_name,
            ast.library_id
        FROM last_event_raw ler
        INNER JOIN allowed_stations ast ON ler.station_id = ast.station_id
    ),
    totals AS (
        SELECT 
            COALESCE(SUM(checkouts), 0) AS total_checkouts,
            COALESCE(SUM(checkins), 0) AS total_checkins
        FROM aggregated_events
    ),
    readers_count AS (
        SELECT COUNT(DISTINCT ef.user_code) AS readers_by_period
        FROM events_filtered ef
        INNER JOIN allowed_stations ast ON ef.station_id = ast.station_id
        WHERE ast.station_type_id IN (1,2,3,5,6,7)
    ),
    last_checkin AS (
        SELECT MAX(date_event) AS last_checkin_date
        FROM events_filtered
        WHERE station_id = (SELECT station_id FROM last_event)
          AND event_id = 11
    ),
    final_data AS (
        SELECT 
            COALESCE((SELECT total_checkins / NULLIF(days_count, 0) FROM totals), 0)::integer AS checkins_per_day,
            COALESCE((SELECT total_checkouts FROM totals), 0)::integer AS checkouts_by_period,
            COALESCE((SELECT total_checkins FROM totals), 0)::integer AS checkins_by_period,
            COALESCE((SELECT total_operations / NULLIF(days_count, 0) FROM most_active), 0)::integer AS max_avg_checkins_per_day,
            COALESCE((SELECT total_operations / NULLIF(days_count, 0) FROM least_active), 0)::integer AS min_avg_checkins_per_day,
            COALESCE((SELECT readers_by_period FROM readers_count), 0)::integer AS readers_by_period,
            COALESCE(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true OR p_library_id IS NOT NULL 
                     THEN (SELECT owner_name || ' / ' || library_name || ' / ' || station_name FROM most_active)
                     ELSE (SELECT library_name || ' / ' || station_name FROM most_active)
                END, '-'
            ) AS most_active_station,
            COALESCE(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true OR p_library_id IS NOT NULL 
                     THEN (SELECT owner_name || ' / ' || library_name || ' / ' || station_name FROM least_active)
                     ELSE (SELECT library_name || ' / ' || station_name FROM least_active)
                END, '-'
            ) AS least_active_station,
            COALESCE(
                CASE WHEN p_owner_id IS NULL OR is_internal_access = true OR p_library_id IS NOT NULL 
                     THEN (SELECT owner_name || ' / ' || library_name || ' / ' || station_name FROM last_event)
                     ELSE (SELECT library_name || ' / ' || station_name FROM last_event)
                END, '-'
            ) AS last_station,
            (SELECT station_id FROM last_event) AS last_station_id,
            (SELECT library_id FROM last_event) AS last_station_library_id,
            (SELECT last_checkin_date FROM last_checkin) AS last_checkin_date
    )
    SELECT 
        checkins_per_day AS "CheckinsPerDay",
        checkouts_by_period AS "CheckoutsByPeriod",
        checkins_by_period AS "CheckinsByPeriod",
        max_avg_checkins_per_day AS "MaxAVGCheckinsPerDay",
        min_avg_checkins_per_day AS "MinAVGCheckinsPerDay",
        readers_by_period AS "ReadersByPeriod",
        LEFT(most_active_station, 500)::text AS "MostActiveStation",
        LEFT(least_active_station, 500)::text AS "LeastActiveStation",
        LEFT(last_station, 500)::text AS "LastStation",
        last_station_id AS "LastStationID",
        last_station_library_id AS "LastStationLibraryID",
        last_checkin_date AS "LastCheckinDate"
    FROM final_data;
END;
$$;


--
-- Name: web_role_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_role_types_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE sql
    AS $$
    SELECT id AS "ID", name AS "Name"
    FROM auth_role_types;
$$;


--
-- Name: web_safe_keeper_admin_history_get(uuid, uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_admin_history_get(p_ownerid uuid, p_libraryid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "CellNumber" integer, "SafeKeeperId" uuid, "SafeKeeperName" text, "EventTypeId" integer, "EventTypeName" text, "AdminRFID" text, "AdminFIO" text, "LibraryID" uuid, "LibraryName" text, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_searchstring IS NULL THEN
        p_searchstring := '';
    END IF;

    RETURN QUERY
    SELECT 
        h.id AS "Id",
        h.date_created AS "DateCreated",
        h.cell_number + (SELECT cell_shift FROM sk_safekeepers WHERE id = h.safe_keeper_id LIMIT 1) AS "CellNumber",
        h.safe_keeper_id AS "SafeKeeperId",
        ms.name AS "SafeKeeperName",
        h.event_type_id AS "EventTypeId",
        e.name AS "EventTypeName",
        mr.rfid AS "AdminRFID",
        h.master_rfid_fio AS "AdminFIO",
        ms.library_id AS "LibraryID",
        l.name AS "LibraryName",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM sk_history_master h
    INNER JOIN sk_safekeepers s ON s.id = h.safe_keeper_id
    INNER JOIN sk_masters_rfid mr ON mr.id = h.master_rfid_id
    INNER JOIN sk_event_types e ON e.id = h.event_type_id
    INNER JOIN mon_stations ms ON ms.id = s.id
    INNER JOIN lib_libraries l ON l.id = ms.library_id
    WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid)) 
        AND (h.date_created::DATE BETWEEN p_datestart AND p_dateend)
        AND (p_searchstring = '' OR ((h.master_rfid_fio ILIKE '%' || p_searchstring || '%') OR (mr.rfid ILIKE '%' || p_searchstring || '%')))
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN h.date_created END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN h.date_created END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN ms.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN ms.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN h.cell_number + (SELECT cell_shift FROM sk_safekeepers WHERE id = h.safe_keeper_id LIMIT 1) END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN h.cell_number + (SELECT cell_shift FROM sk_safekeepers WHERE id = h.safe_keeper_id LIMIT 1) END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN h.master_rfid_fio END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN h.master_rfid_fio END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN mr.rfid END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN mr.rfid END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 5 THEN e.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 5 THEN e.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 6 THEN l.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 6 THEN l.name END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_safe_keeper_by_master_modify(integer, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_by_master_modify(p_opid integer, p_safekeeperid uuid, p_masterrfidid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_opid = 1 THEN
        IF EXISTS (SELECT 1 FROM sk_safekeepers_masters_rfid smr
        INNER JOIN sk_masters_rfid mr ON mr.id = smr.master_rfid_id 
        WHERE safe_keeper_id = p_safekeeperid AND master_rfid_id = p_masterrfidid AND mr.is_deleted = false) THEN
        RETURN;
        END IF;
        INSERT INTO sk_safekeepers_masters_rfid (safe_keeper_id, master_rfid_id)
        VALUES (p_safekeeperid, p_masterrfidid);
    END IF;

    IF p_opid = 0 THEN
        DELETE FROM sk_safekeepers_masters_rfid
        WHERE safe_keeper_id = p_safekeeperid AND master_rfid_id = p_masterrfidid;
    END IF;
END;
$$;


--
-- Name: web_safe_keeper_info_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_info_get(p_safekeeperid uuid, p_ownerid uuid) RETURNS TABLE("StationID" uuid, "StationTypeID" integer, "CellNo" integer, "UserFIO" text, "UserCode" text, "CellContent" text, "DateBusy" timestamp with time zone, "IsBusy" boolean, "CellShift" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    sk RECORD;
    child RECORD;
    i INT;
BEGIN
    -- Проверка прав доступа
    IF NOT EXISTS (
        SELECT 1
        FROM mon_stations s
        JOIN lib_libraries l ON l.id = s.library_id
        WHERE s.id = p_safekeeperid AND l.owner_id = p_ownerid
    ) THEN
        RETURN;
    END IF;

    CREATE TEMP TABLE temp_table (
        station_id UUID,
        station_type_id INTEGER,
        logical_cell_index INTEGER,   -- i (начинается с 1)
        cell_shift INTEGER,
        is_busy BOOLEAN,
        user_fio TEXT,
        user_code TEXT,
        cell_content TEXT,
        date_busy TIMESTAMPTZ
    ) ON COMMIT DROP;

    -- Основной сейф
    FOR sk IN
        SELECT id, safe_keeper_type_id, cells_count, cells_state, cell_shift
        FROM sk_safekeepers
        WHERE id = p_safekeeperid
    LOOP
        i := 1;
        WHILE i < sk.cells_count LOOP  -- как в T-SQL: i от 1 до cells_count - 1
            INSERT INTO temp_table (station_id, station_type_id, logical_cell_index, cell_shift, is_busy)
            VALUES (
                sk.id,
                sk.safe_keeper_type_id,
                i,
                sk.cell_shift,
                (sk.cells_state & (1::BIGINT << (i - 1))) <> 0  -- бит i-1, т.к. бит 0 = ячейка 1
            );
            i := i + 1;
        END LOOP;
    END LOOP;

    -- Дочерние сейфы
    FOR child IN
        SELECT id, safe_keeper_type_id, cells_count, cells_state, cell_shift
        FROM sk_safekeepers
        WHERE parent_id = p_safekeeperid
    LOOP
        i := 1;
        WHILE i < child.cells_count LOOP
            INSERT INTO temp_table (station_id, station_type_id, logical_cell_index, cell_shift, is_busy)
            VALUES (
                child.id,
                child.safe_keeper_type_id,
                i,
                child.cell_shift,
                (child.cells_state & (1::BIGINT << (i - 1))) <> 0
            );
            i := i + 1;
        END LOOP;
    END LOOP;

    -- Обогащение данными (используем logical_cell_index как CellNumber, без сдвига)
    UPDATE temp_table t
    SET
        user_fio = COALESCE(
            (SELECT o.reader_fio
             FROM sk_orders o
             WHERE o.safe_keeper_id = t.station_id
               AND o.cell_number = t.logical_cell_index
               AND o.state_id <= 3
             LIMIT 1),
            (SELECT sc.reader_fio
             FROM sk_storage_cells sc
             WHERE sc.safe_keeper_id = t.station_id
               AND sc.cell_no = t.logical_cell_index
               AND sc.date_close IS NULL
             LIMIT 1)
        ),
        user_code = COALESCE(
            (SELECT o.reader_rfid
             FROM sk_orders o
             WHERE o.safe_keeper_id = t.station_id
               AND o.cell_number = t.logical_cell_index
               AND o.state_id <= 3
             LIMIT 1),
            (SELECT sc.reader_rfid
             FROM sk_storage_cells sc
             WHERE sc.safe_keeper_id = t.station_id
               AND sc.cell_no = t.logical_cell_index
               AND sc.date_close IS NULL
             LIMIT 1)
        ),
        date_busy = COALESCE(
            (SELECT o.date_created
             FROM sk_orders o
             WHERE o.safe_keeper_id = t.station_id
               AND o.cell_number = t.logical_cell_index
               AND o.state_id <= 3
             LIMIT 1),
            (SELECT sc.date_open
             FROM sk_storage_cells sc
             WHERE sc.safe_keeper_id = t.station_id
               AND sc.cell_no = t.logical_cell_index
               AND sc.date_close IS NULL
             LIMIT 1),
            (SELECT vb.date_in
             FROM sk_vending_books vb
             WHERE vb.vending_station_id = t.station_id
               AND vb.cell_number = t.logical_cell_index
             LIMIT 1)
        ),
        cell_content = COALESCE(
            (SELECT 'Заказ №' || o.number::TEXT
             FROM sk_orders o
             WHERE o.safe_keeper_id = t.station_id
               AND o.cell_number = t.logical_cell_index
               AND o.state_id <= 3
             LIMIT 1),
            (SELECT 'Вещи'
             FROM sk_storage_cells sc
             WHERE sc.safe_keeper_id = t.station_id
               AND sc.cell_no = t.logical_cell_index
               AND sc.date_close IS NULL
             LIMIT 1),
            (SELECT vb.book_name
             FROM sk_vending_books vb
             WHERE vb.vending_station_id = t.station_id
               AND vb.cell_number = t.logical_cell_index
             LIMIT 1)
        )
    WHERE t.is_busy = true;

    RETURN QUERY
    SELECT
        t.station_id AS "StationID",
        t.station_type_id AS "StationTypeID",
        (t.logical_cell_index + t.cell_shift) AS "CellNo",  -- КЛЮЧЕВОЕ ИЗМЕНЕНИЕ
        t.user_fio AS "UserFIO",
        t.user_code AS "UserCode",
        t.cell_content AS "CellContent",
        t.date_busy AS "DateBusy",
        t.is_busy AS "IsBusy",
        t.cell_shift AS "CellShift"
    FROM temp_table t
    ORDER BY t.cell_shift, t.logical_cell_index;
END;
$$;


--
-- Name: web_safe_keeper_master_modify(integer, integer, text, text, text, text, uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_master_modify(p_opid integer, p_id integer, p_rfid text, p_firstname text, p_secondname text, p_lastname text, p_ownerid uuid, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_opid = 1 THEN
        IF EXISTS (SELECT 1 FROM sk_masters_rfid WHERE rfid = p_rfid AND is_deleted = false AND owner_id = p_ownerid) THEN
            new_id := -1;
            RETURN;
        END IF;

        INSERT INTO sk_masters_rfid (rfid, first_name, second_name, last_name, owner_id)
        VALUES (p_rfid, p_firstname, p_secondname, p_lastname, p_ownerid)
        RETURNING id INTO new_id;
    ELSIF p_opid = 2 THEN
        UPDATE sk_masters_rfid
        SET rfid = p_rfid,
            first_name = p_firstname,
            second_name = p_secondname,
            last_name = p_lastname
        WHERE id = p_id;
        new_id := p_id;
    ELSIF p_opid = 0 THEN
        UPDATE sk_masters_rfid
        SET is_deleted = true
        WHERE id = p_id;
        new_id := p_id;
    END IF;
END;
$$;


--
-- Name: web_safe_keeper_masters_get(uuid, text, integer, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_masters_get(p_ownerid uuid, p_searchstring text, p_sortid integer, p_filterid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "RFID" text, "FirstName" text, "SecondName" text, "LastName" text, "IsDeleted" boolean, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_searchstring IS NULL THEN
        p_searchstring := '';
    END IF;

    RETURN QUERY
    SELECT 
        id AS "Id",
        date_created AS "DateCreated",
        rfid AS "RFID",
        first_name AS "FirstName",
        second_name AS "SecondName",
        last_name AS "LastName",
        is_deleted AS "IsDeleted",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM sk_masters_rfid
    WHERE owner_id = p_ownerid
        AND is_deleted IN (
            (CASE WHEN p_filterid = 0 THEN false ELSE true END),
            false
        )
        AND (
            p_searchstring = '' 
            OR rfid ILIKE '%' || p_searchstring || '%' 
            OR first_name ILIKE '%' || p_searchstring || '%' 
            OR second_name ILIKE '%' || p_searchstring || '%' 
            OR last_name ILIKE '%' || p_searchstring || '%'
        )
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN last_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN last_name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN first_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN first_name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN second_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN second_name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN rfid END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN rfid END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN date_created END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN date_created END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_safe_keeper_order_body_get(integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_order_body_get(p_order_id integer) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "BookRFID" text, "BookInfo" text, "IsVerified" boolean, "DateVerified" timestamp with time zone, "IsProcessed" boolean, "DateProcessed" timestamp with time zone, "OrderId" integer, "ModifierId" integer, "OrderNumber" text, "ReaderFIO" text, "ReaderRFID" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        b.id            AS "Id",
        b.date_created  AS "DateCreated",
        b.book_rfid     AS "BookRFID",
        b.book_info     AS "BookInfo",
        b.is_verified   AS "IsVerified",
        b.date_verified AS "DateVerified",
        b.is_processed  AS "IsProcessed",
        b.date_processed AS "DateProcessed",
        b.order_id      AS "OrderId",
        b.modifier_id   AS "ModifierId",
        o.number        AS "OrderNumber",
        o.reader_fio    AS "ReaderFIO",
        o.reader_rfid   AS "ReaderRFID"
    FROM sk_order_body b
    INNER JOIN sk_orders o ON o.id = b.order_id
    WHERE b.order_id = p_order_id;
END;
$$;


--
-- Name: web_safe_keeper_order_body_modify(integer, integer, integer, text, text, boolean, boolean, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_order_body_modify(p_opid integer, p_id integer, p_orderid integer, p_bookrfid text, p_bookinfo text, p_isverified boolean, p_isprocessed boolean, p_modifierid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_opid = 1 
    AND EXISTS (SELECT 1 FROM sk_orders WHERE id = p_orderid AND state_id < 3 LIMIT 1)
    THEN
        IF NOT EXISTS (SELECT 1 FROM sk_order_body WHERE order_id = p_orderid AND book_info = p_bookinfo)
        THEN
            INSERT INTO sk_order_body (order_id, book_rfid, book_info, modifier_id)
            VALUES (p_orderid, p_bookrfid, p_bookinfo, p_modifierid);
        END IF;
    END IF;

    IF p_opid = 0 THEN
        DELETE FROM sk_order_body
        WHERE order_id = p_orderid AND id = p_id;
    END IF;
END;
$$;


--
-- Name: web_safe_keeper_order_book_processed_set(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_order_book_processed_set(p_bookrfid text, p_orderid integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    UPDATE sk_OrderBody 
    SET IsProcessed = true, 
        DateProcessed = CURRENT_TIMESTAMP
    WHERE OrderId = p_orderID 
      AND BookRFID = p_bookRFID;
END;
$$;


--
-- Name: web_safe_keeper_order_info_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_order_info_get(p_orderid integer, p_ownerid uuid) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "Number" text, "CellNumber" integer, "ReaderRFID" text, "StateId" integer, "SafeKeeperId" uuid, "ReaderFIO" text, "LoginId" integer, "LoginLastName" text, "LoginFirstName" text, "LoginSecondName" text, "StateName" text, "SafeKeeperName" text, "LibraryID" uuid, "LibraryName" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        o.id AS "Id",
        o.date_created AS "DateCreated",
        o.number AS "Number",
        COALESCE((o.cell_number + k.cell_shift), 0) AS "CellNumber",
        o.reader_rfid AS "ReaderRFID",
        o.state_id AS "StateId",
        (CASE WHEN k.parent_id IS NULL THEN o.safe_keeper_id ELSE (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) END) AS "SafeKeeperId",
        o.reader_fio AS "ReaderFIO",
        o.login_id AS "LoginId",
        al.last_name AS "LoginLastName",
        al.first_name AS "LoginFirstName",
        al.second_name AS "LoginSecondName",
        s.name AS "StateName",
        (CASE WHEN k.parent_id IS NULL THEN ms.name ELSE (SELECT name FROM mon_stations WHERE id = (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) LIMIT 1) END) AS "SafeKeeperName",
        l.id AS "LibraryID",
        l.name AS "LibraryName"
    FROM sk_orders o
    INNER JOIN sk_order_states s ON s.id = o.state_id
    INNER JOIN sk_safekeepers k ON k.id = o.safe_keeper_id
    INNER JOIN auth_logins al ON al.id = o.login_id
    INNER JOIN mon_stations ms ON ms.id = k.id
    INNER JOIN lib_libraries l ON ms.library_id = l.id
    WHERE l.owner_id = p_ownerid AND o.id = p_orderid
    LIMIT 1;
END;
$$;


--
-- Name: web_safe_keeper_order_modify(integer, integer, text, integer, text, text, integer, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_order_modify(p_opid integer, p_id integer, p_number text, p_cellnumber integer, p_readerrfid text, p_readerfio text, p_stateid integer, p_safekeeperid uuid, p_loginid integer, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_target_safekeeper_id UUID;
    v_local_cell_number INTEGER;
    v_cells_state BIGINT;
    bit_mask BIGINT;
    current_keeper UUID;
    current_cell INTEGER;
    rec RECORD;
    v_shift INTEGER;
BEGIN
    -- Операция 0: завершение заказа
    IF p_opid = 0 THEN
        SELECT safe_keeper_id, cell_number
        INTO current_keeper, current_cell
        FROM sk_orders
        WHERE id = p_id
        LIMIT 1;

        IF current_keeper IS NULL THEN
            new_id := -2;  -- заказ не найден
            RETURN;
        END IF;

        -- Устанавливаем статус "завершён"
        UPDATE sk_orders
        SET state_id = 5
        WHERE id = p_id;

        -- Освобождаем ячейку, только если она была занята
        IF current_cell IS NOT NULL THEN
            SELECT cell_shift INTO v_shift
            FROM sk_safekeepers
            WHERE id = current_keeper;

            IF v_shift IS NULL THEN
                new_id := -3;  -- safekeeper удалён
                RETURN;
            END IF;

            v_local_cell_number := current_cell - v_shift;
            IF v_local_cell_number < 1 OR v_local_cell_number > 64 THEN
                new_id := -4;  -- некорректный локальный номер
                RETURN;
            END IF;

            bit_mask := (1::BIGINT << (v_local_cell_number - 1));
            UPDATE sk_safekeepers
            SET cells_state = cells_state & ~bit_mask
            WHERE id = current_keeper;
        END IF;

        new_id := p_id;
        RETURN;
    END IF;

    -- Операция 1: создание нового заказа
    IF p_opid = 1 THEN
        IF p_cellnumber IS NOT NULL THEN
            -- Стандартный режим: ищем safekeeper по ячейке
            v_target_safekeeper_id := NULL;
            FOR rec IN (
                SELECT id, cell_shift, cells_count, cells_state
                FROM sk_safekeepers
                WHERE is_deleted = false
                ORDER BY cell_shift
            ) LOOP
                IF p_cellnumber > rec.cell_shift AND p_cellnumber <= rec.cell_shift + rec.cells_count THEN
                    v_target_safekeeper_id := rec.id;
                    v_local_cell_number := p_cellnumber - rec.cell_shift;
                    v_cells_state := rec.cells_state;
                    EXIT;
                END IF;
            END LOOP;

            IF v_target_safekeeper_id IS NULL THEN
                new_id := -101;  -- ячейка вне диапазонов
                RETURN;
            END IF;

            IF v_local_cell_number < 1 OR v_local_cell_number > 64 THEN
                new_id := -102;
                RETURN;
            END IF;

            bit_mask := (1::BIGINT << (v_local_cell_number - 1));
            IF (v_cells_state & bit_mask) <> 0 THEN
                new_id := -100;  -- ячейка занята
                RETURN;
            END IF;

            INSERT INTO sk_orders (cell_number, reader_rfid, reader_fio, safe_keeper_id, login_id)
            VALUES (p_cellnumber, p_readerrfid, p_readerfio, v_target_safekeeper_id, p_loginid)
            RETURNING id INTO new_id;

        ELSE
            -- Вендинг: ячейка не указана → используем p_safekeeperid
            IF p_safekeeperid IS NULL THEN
                new_id := -101;  -- неоткуда взять станцию
                RETURN;
            END IF;

            -- Проверяем существование safekeeper'а
            IF NOT EXISTS (
                SELECT 1 FROM sk_safekeepers
                WHERE id = p_safekeeperid AND is_deleted = false
            ) THEN
                new_id := -101;  -- безопасно: считаем, что "ячейка вне диапазонов"
                RETURN;
            END IF;

            -- Создаём заказ без ячейки
            INSERT INTO sk_orders (cell_number, reader_rfid, reader_fio, safe_keeper_id, login_id)
            VALUES (NULL, p_readerrfid, p_readerfio, p_safekeeperid, p_loginid)
            RETURNING id INTO new_id;
        END IF;

        -- Генерация номера заказа
        UPDATE sk_orders
        SET number = SUBSTRING(CAST(100000000 + new_id AS TEXT), 2, 8)
        WHERE id = new_id;

        -- Обновляем cells_state только для заказов с ячейкой
        IF p_cellnumber IS NOT NULL THEN
            UPDATE sk_safekeepers
            SET cells_state = cells_state | bit_mask
            WHERE id = v_target_safekeeper_id;
        END IF;

        RETURN;
    END IF;

    -- Операция 2: обновление состояния заказа
    IF p_opid = 2 THEN
        UPDATE sk_orders
        SET state_id = p_stateid,
            login_id = p_loginid
        WHERE id = p_id;

        new_id := p_id;
        RETURN;
    END IF;

    -- Неподдерживаемый opid
    new_id := -99;
    RETURN;

EXCEPTION
    WHEN OTHERS THEN
        new_id := -1;
        RAISE;
END;
$$;


--
-- Name: web_safe_keeper_user_history_get(uuid, uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keeper_user_history_get(p_ownerid uuid, p_libraryid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "CellNumber" integer, "SafeKeeperId" uuid, "SafeKeeperName" text, "EventTypeId" integer, "EventTypeName" text, "ReaderRFID" text, "ReaderFIO" text, "LibraryID" uuid, "LibraryName" text, "RowsCount" bigint)
    LANGUAGE plpgsql
    AS $$
BEGIN
    p_datestart := p_datestart::DATE;
    p_dateend := p_dateend::DATE;
    
    IF p_searchstring IS NULL THEN
        p_searchstring := '';
    END IF;

    RETURN QUERY
    SELECT 
        h.id AS "Id",
        h.date_created AS "DateCreated",
        h.cell_number + (SELECT cell_shift FROM sk_safekeepers WHERE id = h.safe_keeper_id LIMIT 1) AS "CellNumber",
        h.safe_keeper_id AS "SafeKeeperId",
        ms.name AS "SafeKeeperName",
        h.event_type_id AS "EventTypeId",
        e.name AS "EventTypeName",
        h.reader_rfid AS "ReaderRFID",
        h.reader_fio AS "ReaderFIO",
        ms.library_id AS "LibraryID",
        l.name AS "LibraryName",
        COUNT(1) OVER () as "RowsCount"
    FROM sk_history h
    INNER JOIN sk_safekeepers s ON s.id = h.safe_keeper_id
    INNER JOIN sk_event_types e ON e.id = h.event_type_id
    INNER JOIN mon_stations ms ON ms.id = s.id
    INNER JOIN lib_libraries l ON l.id = ms.library_id
    WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid))  
        AND 
    (h.date_created::DATE BETWEEN p_datestart AND p_dateend)
        AND
     (p_searchstring = '' OR ((h.reader_fio LIKE '%' || p_searchstring || '%') OR (h.reader_rfid LIKE '%' || p_searchstring || '%')))
     ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN h.date_created END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN h.date_created END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN ms.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN ms.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN h.cell_number + (SELECT cell_shift FROM sk_safekeepers WHERE id = h.safe_keeper_id LIMIT 1) END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN h.cell_number + (SELECT cell_shift FROM sk_safekeepers WHERE id = h.safe_keeper_id LIMIT 1) END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN h.reader_fio END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN h.reader_fio END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN h.reader_rfid END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN h.reader_rfid END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 5 THEN e.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 5 THEN e.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 6 THEN l.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 6 THEN l.name END DESC NULLS LAST
     OFFSET p_rowsoffset
     LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_safe_keepers_by_master_id_get(uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keepers_by_master_id_get(p_ownerid uuid, p_masterid integer) RETURNS TABLE("SafeKeeperID" uuid, "SafeKeeperName" text, "IsAllow" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_masterid IS NOT NULL THEN
        RETURN QUERY
        SELECT DISTINCT
            sk.id AS "SafeKeeperID",
            (l.name || ' / ' || s.name) AS "SafeKeeperName",
            EXISTS (
                SELECT 1 
                FROM sk_safekeepers_masters_rfid 
                WHERE master_rfid_id = p_masterid 
                  AND safe_keeper_id = sk.id
                LIMIT 1
            ) AS "IsAllow"
        FROM sk_safekeepers sk
        INNER JOIN mon_stations s ON s.id = sk.id
        INNER JOIN lib_libraries l ON l.id = s.library_id
        WHERE l.owner_id = p_ownerid
        ORDER BY "SafeKeeperName";
    ELSE
        RETURN QUERY
        SELECT DISTINCT
            sk.id AS "SafeKeeperID",
            (l.name || ' / ' || s.name) AS "SafeKeeperName",
            false AS "IsAllow"
        FROM sk_safekeepers sk
        INNER JOIN mon_stations s ON s.id = sk.id
        INNER JOIN lib_libraries l ON l.id = s.library_id
        WHERE l.owner_id = p_ownerid AND s.station_type_id = 4
        ORDER BY "SafeKeeperName";
    END IF;
END;
$$;


--
-- Name: web_safe_keepers_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keepers_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" uuid, "DateCreated" timestamp with time zone, "Name" text, "CellsCount" integer, "SerialNumber" text, "CellsState" bigint, "IPAddress" text, "LastUpdate" timestamp with time zone, "IsOnline" boolean, "TypeID" integer, "TypeName" text, "LibraryID" uuid, "LibraryName" text, "CellShift" integer, "BusyCellsCount" integer, "VacantCellsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        s.id AS "ID",
        s.date_created AS "DateCreated",
        s.name AS "Name",
        (
            COALESCE((SELECT SUM(cells_count) FROM sk_safekeepers WHERE parent_id = sk.id), 0)::int
            + sk.cells_count
            - (CASE WHEN sk.is_new_keeper THEN (SELECT COUNT(id) FROM sk_safekeepers WHERE parent_id = sk.id)::int ELSE 0 END)
            - (CASE WHEN sk.is_new_keeper THEN 1 ELSE 0 END)
        ) AS "CellsCount",
        s.sn AS "SerialNumber",
        sk.cells_state AS "CellsState",
        sk.ip_address AS "IPAddress",
        COALESCE(s.last_update, '2000-01-01'::TIMESTAMP) AS "LastUpdate",
        CASE 
            WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.last_update)) / 60, 3) < 2 THEN true 
            ELSE false 
        END AS "IsOnline",
        sk.safe_keeper_type_id AS "TypeID",
        t.name AS "TypeName",
        l.id AS "LibraryID",
        l.name AS "LibraryName",
        sk.cell_shift AS "CellShift",
        (sk.cells_count - LENGTH(REPLACE(sk.cells_state::TEXT, '0', '')) - 1) AS "BusyCellsCount",
        CASE 
            WHEN sk.cells_count IS NOT NULL THEN 
                (sk.cells_count - (sk.cells_count - LENGTH(REPLACE(sk.cells_state::TEXT, '0', '')) - 1)) 
            ELSE 0 
        END AS "VacantCellsCount"
    FROM mon_stations s
    INNER JOIN sk_safekeepers sk ON s.id = sk.id
    INNER JOIN lib_libraries l ON l.id = s.library_id
    INNER JOIN sk_safekeeper_types t ON t.id = sk.safe_keeper_type_id
    WHERE sk.is_deleted = false 
        AND ((p_libraryid IS NOT NULL AND l.id = p_libraryid) 
             OR (p_libraryid IS NULL AND l.owner_id = p_ownerid))
        AND sk.parent_id IS NULL 
        AND sk.safe_keeper_type_id <> 5 
        AND s.station_type_id <> 9;
END;
$$;


--
-- Name: web_safe_keepers_orders_get(uuid, uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keepers_orders_get(p_libraryid uuid, p_ownerid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "Number" text, "CellNumber" integer, "ReaderRFID" text, "StateId" integer, "SafeKeeperId" uuid, "ReaderFIO" text, "LoginId" integer, "LoginLastName" text, "LoginFirstName" text, "LoginSecondName" text, "StateName" text, "SafeKeeperName" text, "LibraryID" uuid, "LibraryName" text, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    p_datestart := p_datestart::DATE;
    p_dateend := p_dateend::DATE;
    
    IF p_searchstring IS NULL THEN
        p_searchstring := '';
    END IF;

    RETURN QUERY
    SELECT 
        o.id AS "Id",
        o.date_created AS "DateCreated",
        o.number AS "Number",
        (o.cell_number + k.cell_shift) AS "CellNumber",
        o.reader_rfid AS "ReaderRFID",
        o.state_id AS "StateId",
        (CASE WHEN k.parent_id IS NULL THEN o.safe_keeper_id ELSE (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) END) AS "SafeKeeperId",
        o.reader_fio AS "ReaderFIO",
        o.login_id AS "LoginId",
        al.last_name AS "LoginLastName",
        al.first_name AS "LoginFirstName",
        al.second_name AS "LoginSecondName",
        s.name AS "StateName",
        (CASE WHEN k.parent_id IS NULL THEN ms.name ELSE (SELECT name FROM mon_stations WHERE id = (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) LIMIT 1) END) AS "SafeKeeperName",
        l.id AS "LibraryID",
        l.name AS "LibraryName",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM sk_orders o
    INNER JOIN sk_order_states s ON s.id = o.state_id
    INNER JOIN sk_safekeepers k ON k.id = o.safe_keeper_id
    INNER JOIN auth_logins al ON al.id = o.login_id
    INNER JOIN mon_stations ms ON ms.id = k.id
    INNER JOIN lib_libraries l ON ms.library_id = l.id
    WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid)) 
        AND ms.station_type_id <> 9
        AND (o.date_created::DATE BETWEEN p_datestart AND p_dateend)
        AND (p_searchstring = '' OR ((o.reader_fio ILIKE '%' || p_searchstring || '%') OR (o.number ILIKE '%' || p_searchstring || '%')))
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN (SELECT name FROM mon_stations WHERE id = (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) LIMIT 1) END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN (SELECT name FROM mon_stations WHERE id = (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) LIMIT 1) END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN o.number END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN o.number END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN o.date_created END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN o.date_created END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN o.cell_number END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN o.cell_number END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN o.reader_fio END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN o.reader_fio END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 5 THEN s.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 5 THEN s.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 6 THEN l.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 6 THEN l.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 7 THEN al.last_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 7 THEN al.last_name END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_safe_keepers_orders_states_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keepers_orders_states_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        os.id::int AS "ID",
        os.name::text AS "Name"
    FROM sk_order_states os;
END;
$$;


--
-- Name: web_safe_keepers_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_safe_keepers_types_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        skt.id::int AS "ID",
        skt.name::text AS "Name"
    FROM sk_safekeeper_types skt;
END;
$$;


--
-- Name: web_services_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_services_types_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        st.id::int AS "ID",
        st.name::text AS "Name"
    FROM mon_servicetypes st;
END;
$$;


--
-- Name: web_smart_shelf_books_online_get(uuid, text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_smart_shelf_books_online_get(p_stationid uuid, p_searchstring text, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "BookID" integer, "BookName" text, "BookCode" text, "CountTakes" integer, "BookRate" integer, "BookRateCount" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        bs.id AS "ID",
        bs.book_id AS "BookID",
        b.book_name AS "BookName",
        b.book_code AS "BookCode",
        bs.count_takes AS "CountTakes",
        COALESCE(r.avg_rate, 0)::INTEGER AS "BookRate",           -- ← приведение к INTEGER
        COALESCE(r.rate_count, 0)::INTEGER AS "BookRateCount",    -- ← приведение к INTEGER
        COUNT(*) OVER ()::INTEGER AS "RowsCount"                  -- ← приведение к INTEGER
    FROM mon_book_shelves_stat bs
    INNER JOIN lib_books b ON b.id = bs.book_id
    LEFT JOIN (
        SELECT 
            book_id,
            AVG(rate)::NUMERIC AS avg_rate,   -- AVG даёт numeric, но мы потом приведём к int
            COUNT(id) AS rate_count
        FROM lib_book_user_ratings
        GROUP BY book_id
    ) r ON r.book_id = b.id
    WHERE 
        bs.station_id = p_stationid
        AND bs.is_online = true
        AND (
            p_searchstring IS NULL 
            OR p_searchstring = ''
            OR b.book_name ILIKE '%' || p_searchstring || '%'
            OR b.book_code ILIKE '%' || p_searchstring || '%'
        )
    ORDER BY b.book_name
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_smart_shelf_dashboard_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_smart_shelf_dashboard_get(p_stationid uuid) RETURNS TABLE("TakesCountToday" integer, "TakesCountOverall" integer, "PutCountToday" integer, "PutCountOverall" integer, "ReturnsCountToday" integer, "ReturnsCountOverall" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    day DATE := CURRENT_DATE;
BEGIN
    RETURN QUERY
    SELECT 
        (SELECT COUNT(id)::INTEGER FROM mon_stations_event_history WHERE station_id = p_stationid AND event_id = 15 AND date_event::date = day) AS "TakesCountToday",
        (SELECT COUNT(id)::INTEGER FROM mon_stations_event_history WHERE station_id = p_stationid AND event_id = 15) AS "TakesCountOverall",
        (SELECT COUNT(id)::INTEGER FROM mon_stations_event_history WHERE station_id = p_stationid AND event_id = 16 AND date_event::date = day) AS "PutCountToday",
        (SELECT COUNT(id)::INTEGER FROM mon_stations_event_history WHERE station_id = p_stationid AND event_id = 16) AS "PutCountOverall",
        (SELECT COUNT(id)::INTEGER FROM mon_stations_event_history WHERE station_id = p_stationid AND event_id = 12 AND date_event::date = day) AS "ReturnsCountToday",
        (SELECT COUNT(id)::INTEGER FROM mon_stations_event_history WHERE station_id = p_stationid AND event_id = 12) AS "ReturnsCountOverall";
END;
$$;


--
-- Name: web_smart_shelf_returns_get(uuid, timestamp with time zone, timestamp with time zone, text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_smart_shelf_returns_get(p_stationid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("DateEvent" timestamp with time zone, "UserCode" text, "BookName" text, "BookCode" text, "ReaderName" text, "BookRate" integer, "BookRateCount" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        eh.date_event AS "DateEvent",
        eh.user_code AS "UserCode",
        b.book_name AS "BookName",
        b.book_code AS "BookCode",
        r.name AS "ReaderName",
        COALESCE(book_rating.avg_rate, 0)::INTEGER AS "BookRate",           
        COALESCE(book_rating.rate_count, 0)::INTEGER AS "BookRateCount",    
        COUNT(*) OVER ()::INTEGER AS "RowsCount"                           
    FROM mon_stations_event_history eh
    INNER JOIN lib_books b ON b.book_code = eh.message
    LEFT JOIN (
        SELECT 
            book_id,
            AVG(rate) AS avg_rate,      
            COUNT(*) AS rate_count
        FROM lib_book_user_ratings
        GROUP BY book_id
    ) book_rating ON book_rating.book_id = b.id
    LEFT JOIN LATERAL (
        SELECT r.name
        FROM rdr_readers r
        WHERE r.abis_code = eh.user_code 
           OR r.rfid_code = eh.user_code 
           OR r.serial_number = eh.user_code
        LIMIT 1
    ) r ON true
    WHERE 
        eh.station_id = p_stationid
        AND eh.event_id = 12
        AND eh.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
        AND (
            p_searchstring IS NULL
            OR p_searchstring = ''
            OR b.book_name ILIKE '%' || p_searchstring || '%'
            OR b.book_code ILIKE '%' || p_searchstring || '%'
        )
    ORDER BY b.book_name
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_smart_shelf_state_get(uuid, text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_smart_shelf_state_get(p_stationid uuid, p_searchstring text, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "BookID" integer, "BookName" text, "BookCode" text, "CountTakes" integer, "BookRate" integer, "BookRateCount" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        bs.id AS "ID",
        bs.book_id AS "BookID",
        b.book_name AS "BookName",
        b.book_code AS "BookCode",
        bs.count_takes AS "CountTakes",
        COALESCE((SELECT AVG(rate)::INTEGER FROM lib_bookuser_ratings WHERE book_id = bs.book_id), 0) AS "BookRate",
        COALESCE((SELECT COUNT(id)::INTEGER FROM lib_bookuser_ratings WHERE book_id = bs.book_id), 0) AS "BookRateCount",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"
    FROM mon_bookshelvesstat bs
    INNER JOIN lib_books b ON b.id = bs.book_id
    WHERE 
        bs.station_id = p_stationid
        AND bs.is_online = true
        AND (
            p_searchstring = '' 
            OR b.book_name ILIKE '%' || p_searchstring || '%' 
            OR b.book_code ILIKE '%' || p_searchstring || '%'
        )
    ORDER BY b.book_name
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_smart_shelves_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_smart_shelves_get(p_library_id uuid, p_owner_id uuid) RETURNS TABLE("ID" uuid, "Name" text, "DateCreated" timestamp with time zone, "LastUpdate" timestamp with time zone, "IsOnline" integer, "SN" text, "TypeID" integer, "TypeName" text, "LibraryID" uuid, "IsReaderOk" integer, "IsCardReaderOk" integer, "IsPrinterOk" integer, "IsABISOk" integer, "IsControllerOk" integer, "IsConveyorOk" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        s.id AS "ID",
        s.name AS "Name",
        s.date_created AS "DateCreated",
        s.last_update AS "LastUpdate",
        CASE 
            WHEN EXTRACT(EPOCH FROM (NOW() - s.last_update)) / 60 < 2 THEN 1 
            ELSE 0 
        END::INTEGER AS "IsOnline",
        s.sn AS "SN",
        st.id AS "TypeID",
        st.name AS "TypeName",
        s.library_id AS "LibraryID",
        CASE 
            WHEN EXTRACT(EPOCH FROM (NOW() - s.monitor_part1)) / 60 < 2 THEN 1 
            ELSE 0 
        END::INTEGER AS "IsReaderOk",
        CASE 
            WHEN EXTRACT(EPOCH FROM (NOW() - s.monitor_part2)) / 60 < 2 THEN 1 
            ELSE 0 
        END::INTEGER AS "IsCardReaderOk",
        CASE 
            WHEN EXTRACT(EPOCH FROM (NOW() - s.monitor_part3)) / 60 < 2 THEN 1 
            ELSE 0 
        END::INTEGER AS "IsPrinterOk",
        CASE 
            WHEN EXTRACT(EPOCH FROM (NOW() - s.monitor_part4)) / 60 < 2 THEN 1 
            ELSE 0 
        END::INTEGER AS "IsABISOk",
        CASE 
            WHEN EXTRACT(EPOCH FROM (NOW() - s.monitor_part5)) / 60 < 2 THEN 1 
            ELSE 0 
        END::INTEGER AS "IsControllerOk",
        CASE 
            WHEN EXTRACT(EPOCH FROM (NOW() - s.monitor_part6)) / 60 < 2 THEN 1 
            ELSE 0 
        END::INTEGER AS "IsConveyorOk"
    FROM mon_stations s
    INNER JOIN mon_station_types st ON st.id = s.station_type_id
    INNER JOIN lib_libraries l ON l.id = s.library_id
    WHERE (
        (p_library_id IS NOT NULL AND l.id = p_library_id) 
        OR 
        (p_library_id IS NULL AND l.owner_id = p_owner_id)
    )
    AND s.station_type_id IN (3, 8)
    ORDER BY s.name;
END;
$$;


--
-- Name: web_station_alive_monitoring_get(uuid, uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_station_alive_monitoring_get(p_ownerid uuid, p_stationid uuid, p_date timestamp with time zone) RETURNS TABLE("HourValue" integer, "AlivePercents" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    WITH hours AS (
        SELECT 
            EXTRACT(HOUR FROM (CURRENT_TIMESTAMP + INTERVAL '1 hour' * gs.h))::INTEGER AS hour_value
        FROM generate_series(-1, 23) AS gs(h)
    ),
    alive_data AS (
        SELECT 
            EXTRACT(HOUR FROM sm.date_created)::INTEGER AS hour_value,
            COUNT(*) AS count_sessions
        FROM 
            mon_stations_monitoring sm
        INNER JOIN mon_stations s ON s.id = sm.station_id
        INNER JOIN lib_libraries l ON l.id = s.library_id
        WHERE 
            sm.date_created >= (p_date - INTERVAL '1 day')
            AND sm.station_id = p_stationid
            AND l.owner_id = p_ownerid
        GROUP BY 
            EXTRACT(HOUR FROM sm.date_created)::INTEGER
    )
    SELECT 
        h.hour_value,
        CASE 
            WHEN p_date::DATE > CURRENT_DATE THEN -1
            WHEN h.hour_value > EXTRACT(HOUR FROM CURRENT_TIMESTAMP)::INTEGER 
                 AND p_date::DATE = CURRENT_DATE THEN -1
            ELSE GREATEST(LEAST(ROUND(COALESCE(ad.count_sessions, 0) * 100.0 / 180.0)::INTEGER, 100), 0)
        END AS "AlivePercents"
    FROM 
        hours h
    LEFT JOIN 
        alive_data ad ON h.hour_value = ad.hour_value
    ORDER BY 
        h.hour_value;
END;
$$;


--
-- Name: web_station_banner_modify(integer, text, timestamp with time zone, timestamp with time zone, integer, bytea, boolean, boolean, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_station_banner_modify(p_bannerid integer, p_bannername text, p_periodfrom timestamp with time zone, p_periodto timestamp with time zone, p_interval integer, p_image bytea, p_isdeleted boolean, p_isenabled boolean, p_loginid integer, p_opid integer, INOUT new_id integer DEFAULT '-1'::integer) RETURNS integer
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_opid = 1 THEN
        INSERT INTO mon_stations_banners (name, period_from, period_to, interval, image, login_id, is_enabled)
        VALUES (p_bannername, p_periodfrom, p_periodto, p_interval, p_image, p_loginid, p_isenabled)
        RETURNING id INTO new_id;
    ELSIF p_opid = 2 THEN
        UPDATE mon_stations_banners
        SET name = p_bannername,
            period_from = p_periodfrom,
            period_to = p_periodto,
            interval = p_interval,
            image = p_image,
            is_enabled = p_isenabled
        WHERE id = p_bannerid AND login_id = p_loginid;
        new_id := p_bannerid;
    ELSIF p_opid = 0 THEN
        UPDATE mon_stations_banners
        SET is_deleted = true
        WHERE id = p_bannerid AND login_id = p_loginid;
        new_id := p_bannerid;
    END IF;
END;
$$;


--
-- Name: web_station_banners_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_station_banners_get(p_ownerid uuid) RETURNS TABLE("ID" integer, "Name" text, "DateCreated" timestamp with time zone, "PeriodFrom" timestamp with time zone, "PeriodTo" timestamp with time zone, "Interval" integer, "Image" bytea, "LoginID" integer, "IsDeleted" boolean, "LoginName" text, "IsEnabled" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        sb.id AS "ID",
        sb.name AS "Name",
        sb.date_created AS "DateCreated",
        sb.period_from AS "PeriodFrom",
        sb.period_to AS "PeriodTo",
        sb.interval AS "Interval",
        sb.image AS "Image",
        sb.login_id AS "LoginID",
        sb.is_deleted AS "IsDeleted",
        (sb.name || ' ' || sb.login_id::VARCHAR) AS "LoginName",
        sb.is_enabled AS "IsEnabled"
    FROM mon_stations_banners sb
    INNER JOIN auth_logins l ON l.id = sb.login_id
    WHERE l.owner_id = p_ownerid AND sb.is_deleted = false;
END;
$$;


--
-- Name: web_station_modify(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_station_modify(p_id uuid, p_name text, p_libraryid uuid) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    UPDATE mon_stations
    SET name = p_name,
        library_id = p_libraryid
    WHERE id = p_id;
END;
$$;


--
-- Name: web_station_monitoring_get(uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_station_monitoring_get(p_stationid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_filterid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "StationID" uuid, "DateEvent" timestamp with time zone, "EventID" integer, "EventName" text, "EventTypeID" integer, "EventTypeName" text, "Message" text, "Count" integer, "UserCode" text, "PairID" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        sd.id AS "ID",
        sd.station_id AS "StationID",
        sd.date_event AS "DateEvent",
        sd.event_id AS "EventID",
        e.name AS "EventName",
        e.event_type_id AS "EventTypeID",
        et.name AS "EventTypeName",
        sd.message AS "Message",
        sd.count AS "Count",
        sd.user_code AS "UserCode",
        sd.pair_id AS "PairID",
        COUNT(*) OVER ()::INTEGER AS "RowsCount"  
    FROM mon_stations_event_history sd
    INNER JOIN mon_events e ON e.id = sd.event_id
    INNER JOIN mon_event_types et ON et.id = e.event_type_id
    WHERE sd.station_id = p_stationid
      AND sd.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
      AND sd.event_id IN (
          1, 3, 4, 5, 6, 7, 17, 18,
          27, 28, 29, 30, 31, 32, 33, 34, 35,
          39, 40, 41, 43, 44, 47, 52, 56, 57
      )
      AND (
            p_filterid <= 0
            OR et.id = p_filterid
          )
      AND (
            p_searchstring IS NULL
            OR p_searchstring = ''
            OR e.name ILIKE '%' || p_searchstring || '%'
            OR et.name ILIKE '%' || p_searchstring || '%'
            OR sd.message ILIKE '%' || p_searchstring || '%'
            OR sd.user_code ILIKE '%' || p_searchstring || '%'
          )
    ORDER BY
        CASE WHEN p_sortid = 0 AND p_sortdirection = 0 THEN sd.date_event END ASC,
        CASE WHEN p_sortid = 0 AND p_sortdirection = 1 THEN sd.date_event END DESC,
        CASE WHEN p_sortid = 1 AND p_sortdirection = 0 THEN e.name END ASC,
        CASE WHEN p_sortid = 1 AND p_sortdirection = 1 THEN e.name END DESC
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_station_stats_get(uuid, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_station_stats_get(p_stationid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone) RETURNS TABLE("AvgSessionDurationSec" integer, "SessionCount" integer, "ReadersCount" integer, "ReaderDeclineCount" integer, "ReaderAccountCount" integer, "BookBorrowCount" integer, "BookReturnCount" integer, "BookRenewCount" integer, "PrintCount" integer, "RateCount" integer, "BooksPutCount" integer, "BooksSearchCount" integer, "AvgBookBorrowCountDay" integer, "AvgBookReturnCountDay" integer, "AvgReadersCountDay" integer, "AvgBooksPutDay" integer, "AvgRateCountDay" integer, "AvgBooksSearchCountDay" integer, "RenewCountByAccountPercent" integer, "PrintCountBySessionPercent" integer, "LoginSuccessPercent" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    v_station_type_id INTEGER;
    date_diff INTEGER;
BEGIN
    SELECT station_type_id INTO v_station_type_id 
    FROM mon_stations 
    WHERE id = p_stationid 
    LIMIT 1;

    IF v_station_type_id IS NULL THEN
        v_station_type_id := 0;
    END IF;

    date_diff := CASE 
        WHEN p_datestart >= p_dateend THEN 1 
        ELSE (p_dateend::date - p_datestart::date) + 1 
    END;

    RETURN QUERY
    WITH v AS (
        SELECT 
            COALESCE(AVG(EXTRACT(EPOCH FROM (e1.date_event - e2.date_event))), 0)::INTEGER AS avg_session_duration_sec,
            COUNT(e1.id)::INTEGER AS session_count,
            (SELECT COUNT(DISTINCT user_code)::INTEGER 
             FROM mon_stations_event_history 
             WHERE date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
               AND station_id = p_stationid) AS readers_count
        FROM mon_stations_event_history e1
        INNER JOIN mon_stations_event_history e2 ON e1.pair_id = e2.id
        WHERE e1.event_id = 26 
          AND e1.station_id = p_stationid 
          AND e1.date_event BETWEEN p_datestart AND p_dateend
    ),
    v2 AS (
        SELECT 
            COALESCE(SUM(CASE WHEN e1.event_id = 8 THEN 1 ELSE 0 END), 0)::INTEGER AS reader_decline_count,
            COALESCE(SUM(CASE WHEN e1.event_id = 9 THEN 1 ELSE 0 END), 0)::INTEGER AS reader_account_count,
            COALESCE(SUM(CASE 
                WHEN (v_station_type_id = 9 AND e1.event_id = 51) OR 
                     (v_station_type_id <> 9 AND e1.event_id = 10) 
                THEN 1 
                ELSE 0 
            END), 0)::INTEGER AS book_borrow_count,
            COALESCE(SUM(CASE WHEN e1.event_id = 11 THEN 1 ELSE 0 END), 0)::INTEGER AS book_return_count,
            COALESCE(SUM(CASE WHEN e1.event_id = 13 THEN 1 ELSE 0 END), 0)::INTEGER AS book_renew_count,
            COALESCE(SUM(CASE WHEN e1.event_id = 14 THEN 1 ELSE 0 END), 0)::INTEGER AS print_count,
            COALESCE(SUM(CASE WHEN e1.event_id = 49 THEN 1 ELSE 0 END), 0)::INTEGER AS rate_count,
            COALESCE(SUM(CASE WHEN e1.event_id = 50 THEN 1 ELSE 0 END), 0)::INTEGER AS books_put_count,
            COALESCE(SUM(CASE WHEN e1.event_id = 54 THEN 1 ELSE 0 END), 0)::INTEGER AS books_search_count
        FROM mon_stations_event_history e1  
        WHERE e1.event_id IN (8, 9, 10, 11, 13, 14, 49, 50, 51, 54)
          AND e1.station_id = p_stationid 
          AND e1.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
    ),
    v3 AS (
        SELECT (COUNT(e1.id) / date_diff)::INTEGER AS avg_book_borrow_count_day
        FROM mon_stations_event_history e1 
        WHERE e1.event_id = 10 
          AND e1.station_id = p_stationid 
          AND e1.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
    ),
    v4 AS (
        SELECT (COUNT(e1.id) / date_diff)::INTEGER AS avg_book_return_count_day
        FROM mon_stations_event_history e1 
        WHERE e1.event_id = 11 
          AND e1.station_id = p_stationid 
          AND e1.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
    ),
    vvv AS (
        SELECT (COUNT(DISTINCT e1.user_code) / date_diff)::INTEGER AS avg_readers_count_day
        FROM mon_stations_event_history e1 
        WHERE e1.station_id = p_stationid 
          AND e1.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
    ),
    vvvv AS (
        SELECT (COUNT(e1.id) / date_diff)::INTEGER AS avg_books_put_day
        FROM mon_stations_event_history e1 
        WHERE e1.event_id = 50
          AND e1.station_id = p_stationid
          AND e1.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
    ),
    v5 AS (
        SELECT COALESCE(
            (CASE 
                WHEN (p_dateend::date - p_datestart::date) > 0 THEN SUM(rate_count) / (p_dateend::date - p_datestart::date) 
                ELSE 0 
            END)::INTEGER, 0) AS avg_rate_count_day
        FROM (
            SELECT e1.date_event::DATE AS date_event, COUNT(id)::INTEGER AS rate_count
            FROM mon_stations_event_history e1 
            WHERE e1.event_id = 49 
              AND e1.station_id = p_stationid 
              AND e1.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
            GROUP BY e1.date_event::DATE
        ) sub_v
    ),
    v6 AS (
        SELECT (COUNT(e1.id) / date_diff)::INTEGER AS avg_books_search_count_day  
        FROM mon_stations_event_history e1 
        WHERE e1.event_id = 54
          AND e1.station_id = p_stationid
          AND e1.date_event::DATE BETWEEN p_datestart::DATE AND p_dateend::DATE
    )
    SELECT 
        v.avg_session_duration_sec,
        v.session_count,
        v.readers_count,
        v2.reader_decline_count,
        v2.reader_account_count,
        v2.book_borrow_count,
        v2.book_return_count,
        v2.book_renew_count,
        v2.print_count,
        v2.rate_count,
        v2.books_put_count,
        v2.books_search_count,
        v3.avg_book_borrow_count_day,
        v4.avg_book_return_count_day,
        vvv.avg_readers_count_day,
        vvvv.avg_books_put_day,
        v5.avg_rate_count_day,
        v6.avg_books_search_count_day, 
        CASE 
            WHEN v2.reader_account_count <> 0 THEN 
                LEAST(ROUND((v2.book_renew_count::DECIMAL / v2.reader_account_count) * 100)::INTEGER, 100)
            ELSE 0 
        END AS renew_count_by_account_percent,
        CASE 
            WHEN v.session_count <> 0 THEN 
                LEAST(ROUND((v2.print_count::DECIMAL / v.session_count) * 100)::INTEGER, 100)
            ELSE 0 
        END AS print_count_by_session_percent,
        100 - CASE 
                 WHEN v.session_count <> 0 THEN 
                    LEAST(ROUND((v2.reader_decline_count::DECIMAL / v.session_count) * 100)::INTEGER, 100)
                 ELSE 100 
             END AS login_success_percent
    FROM v, v2, v3, v4, vvv, vvvv, v5, v6;  
END;
$$;


--
-- Name: web_station_to_banner_attach(integer, uuid, uuid, boolean); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_station_to_banner_attach(p_bannerid integer, p_stationid uuid, p_ownerid uuid, p_opid boolean) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM mon_stations_banners_tasks WHERE banner_id = p_bannerid AND station_id = p_stationid LIMIT 1) THEN
        IF p_opid = true THEN
            INSERT INTO mon_stations_banners_tasks (banner_id, station_id, owner_id)
            VALUES (p_bannerid, p_stationid, p_ownerid);
        END IF;
    ELSE
        IF p_opid = false THEN
            DELETE FROM mon_stations_banners_tasks
            WHERE banner_id = p_bannerid AND station_id = p_stationid AND owner_id = p_ownerid;
        END IF;
    END IF;
END;
$$;


--
-- Name: web_stations_by_banner_id_get(integer, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_stations_by_banner_id_get(p_bannerid integer, p_ownerid uuid) RETURNS TABLE("ID" uuid, "StationName" text, "IsChecked" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
	RETURN QUERY
		WITH tab AS (
			SELECT s.id AS id,
				(l.name || ' / ' || s.name) AS station_name,
				CASE WHEN EXISTS (SELECT 1 FROM mon_stations_banners_tasks sbt WHERE sbt.banner_id = p_bannerid AND s.id = sbt.station_id) THEN TRUE ELSE FALSE END AS is_checked
			FROM mon_stations s
			INNER JOIN lib_libraries l ON l.id = s.library_id
			LEFT JOIN sk_safekeepers sk ON sk.id = s.id
			WHERE l.owner_id = p_ownerid AND sk.parent_id IS NULL
		)
		SELECT t.id AS "ID", t.station_name AS "StationName", t.is_checked AS "IsChecked"
		FROM tab t
		ORDER BY t.station_name;
END;
$$;


--
-- Name: web_stations_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_stations_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "DateCreated" timestamp with time zone, "LastUpdate" timestamp with time zone, "IsOnline" boolean, "SN" text, "TypeID" integer, "TypeName" text, "LibraryID" uuid, "LibraryName" text, "IP" text, "IsReaderOk" boolean, "IsCardReaderOk" boolean, "IsPrinterOk" boolean, "IsABISOk" boolean, "IsControllerOk" boolean, "IsConveyorOk" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        s.id                                     AS "ID",
        s.name                                   AS "Name",
        s.date_created                           AS "DateCreated",
        s.last_update                            AS "LastUpdate",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.last_update)) / 60, 3) < 2 THEN true ELSE false END AS "IsOnline",
        s.sn                                     AS "SN",
        st.id                                    AS "TypeID",
        st.name                                  AS "TypeName",
        s.library_id                             AS "LibraryID",
        l.name                                   AS "LibraryName",
        s.ip                                     AS "IP",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part1)) / 60, 3) < 2 THEN true ELSE false END AS "IsReaderOk",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part2)) / 60, 3) < 2 THEN true ELSE false END AS "IsCardReaderOk",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part3)) / 60, 3) < 2 THEN true ELSE false END AS "IsPrinterOk",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part4)) / 60, 3) < 2 THEN true ELSE false END AS "IsABISOk",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part5)) / 60, 3) < 2 THEN true ELSE false END AS "IsControllerOk",
        CASE WHEN COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part6)) / 60, 3) < 2 THEN true ELSE false END AS "IsConveyorOk"
    FROM mon_stations s
    INNER JOIN mon_station_types st ON st.id = s.station_type_id
    INNER JOIN lib_libraries l ON l.id = s.library_id
    WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid)
       OR (p_libraryid IS NULL AND l.owner_id = p_ownerid))
      AND s.station_type_id IN (1, 2, 5, 6, 7, 8)
    ORDER BY s.name;
END;
$$;


--
-- Name: web_stations_history_get(uuid, uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_stations_history_get(p_libraryid uuid, p_ownerid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "DateEvent" timestamp with time zone, "StationID" uuid, "StationName" text, "EventID" integer, "EventName" text, "Message" text, "UserCode" text, "UserName" text, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    event_ids INTEGER[] := ARRAY[8, 9, 10, 11, 13, 14, 23, 25, 26, 49];
    v_datestart timestamptz;
    v_dateend timestamptz;
BEGIN
    -- Нормализуем даты до границ суток
    v_datestart := p_datestart::DATE;
    v_dateend   := (p_dateend::DATE) + INTERVAL '1 day';

    IF p_searchstring IS NULL THEN
        p_searchstring := '';
    END IF;

    RETURN QUERY
    SELECT
        seh.id AS "ID",
        seh.date_event AS "DateEvent",
        seh.station_id AS "StationID",
        s.name AS "StationName",
        seh.event_id AS "EventID",
        e.name AS "EventName",
        seh.message AS "Message",
        seh.user_code AS "UserCode",
        r.name AS "UserName",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM mon_stations_event_history seh
    INNER JOIN mon_stations s 
        ON s.id = seh.station_id 
        AND s.station_type_id NOT IN (3,4,9)
        AND (
            (p_libraryid IS NOT NULL AND s.library_id = p_libraryid)
            OR (p_libraryid IS NULL AND s.library_id IN (
                SELECT id FROM lib_libraries WHERE owner_id = p_ownerid
            ))
        )
    INNER JOIN mon_events e ON e.id = seh.event_id
    LEFT JOIN rdr_readers r ON seh.user_code IN (r.abis_code, r.rfid_code)
    WHERE 
        seh.date_event >= v_datestart
        AND seh.date_event < v_dateend
        AND seh.event_id = ANY(event_ids)
        AND (p_searchstring = '' OR (
            s.name ILIKE '%' || p_searchstring || '%'
            OR e.name ILIKE '%' || p_searchstring || '%'
            OR seh.message ILIKE '%' || p_searchstring || '%'
            OR seh.user_code ILIKE '%' || p_searchstring || '%'
        ))
    ORDER BY
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN seh.date_event END ASC  NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN seh.date_event END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN s.name END DESC  NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN s.name END ASC  NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN e.name END ASC  NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN e.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN seh.message END ASC  NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN seh.message END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN seh.user_code END ASC  NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN seh.user_code END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_stations_types_get(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_stations_types_get() RETURNS TABLE("ID" integer, "Name" text)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT
        st.id::int AS "ID",
        st.name::text AS "Name"
    FROM mon_station_types st;
END;
$$;


--
-- Name: web_vending_order_book_processed_set(text, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_vending_order_book_processed_set(p_book_rfid text, p_order_id integer) RETURNS void
    LANGUAGE plpgsql
    AS $$
BEGIN
    UPDATE sk_order_body 
    SET is_processed = true, 
        date_processed = CURRENT_TIMESTAMP
    WHERE order_id = p_order_id 
      AND book_rfid = p_book_rfid;
END;
$$;


--
-- Name: web_vending_orders_get(uuid, uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_vending_orders_get(p_libraryid uuid, p_ownerid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("Id" integer, "DateCreated" timestamp with time zone, "Number" text, "CellNumber" integer, "ReaderRFID" text, "StateId" integer, "SafeKeeperId" uuid, "ReaderFIO" text, "LoginId" integer, "LoginLastName" text, "LoginFirstName" text, "LoginSecondName" text, "StateName" text, "SafeKeeperName" text, "LibraryID" uuid, "LibraryName" text, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    p_datestart := p_datestart::DATE;
    p_dateend := p_dateend::DATE;

    RETURN QUERY
    SELECT 
        o.id AS "Id",
        o.date_created AS "DateCreated",
        o.number AS "Number",
        (o.cell_number + k.cell_shift) AS "CellNumber",
        o.reader_rfid AS "ReaderRFID",
        o.state_id AS "StateId",
        (CASE WHEN k.parent_id IS NULL THEN o.safe_keeper_id ELSE (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) END) AS "SafeKeeperId",
        o.reader_fio AS "ReaderFIO",
        o.login_id AS "LoginId",
        al.last_name AS "LoginLastName",
        al.first_name AS "LoginFirstName",
        al.second_name AS "LoginSecondName",
        s.name AS "StateName",
        (CASE WHEN k.parent_id IS NULL THEN ms.name ELSE (SELECT name FROM mon_stations WHERE id = (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) LIMIT 1) END) AS "SafeKeeperName",
        l.id AS "LibraryID",
        l.name AS "LibraryName",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM sk_orders o
    INNER JOIN sk_order_states s ON s.id = o.state_id
    INNER JOIN sk_safekeepers k ON k.id = o.safe_keeper_id
    INNER JOIN auth_logins al ON al.id = o.login_id
    INNER JOIN mon_stations ms ON ms.id = k.id
    INNER JOIN lib_libraries l ON ms.library_id = l.id
    WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) OR (p_libraryid IS NULL AND l.owner_id = p_ownerid)) 
        AND ms.station_type_id = 9
        AND (o.date_created::DATE BETWEEN p_datestart AND p_dateend)
        AND (p_searchstring = '' OR ((o.reader_fio ILIKE '%' || p_searchstring || '%') OR (o.number ILIKE '%' || p_searchstring || '%')))
    ORDER BY 
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN (SELECT name FROM mon_stations WHERE id = (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) LIMIT 1) END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN (SELECT name FROM mon_stations WHERE id = (SELECT parent_id FROM sk_safekeepers WHERE id = o.safe_keeper_id LIMIT 1) LIMIT 1) END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN o.number END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN o.number END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN o.date_created END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN o.date_created END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN o.cell_number END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN o.cell_number END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN o.reader_fio END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN o.reader_fio END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 5 THEN s.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 5 THEN s.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 6 THEN l.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 6 THEN l.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 7 THEN al.last_name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 7 THEN al.last_name END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_vending_state_get(uuid, text, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_vending_state_get(p_stationid uuid, p_searchstring text, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "BookName" text, "BookCode" text, "CellNumber" integer, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT 
        vb.id AS "ID",
        vb.book_name AS "BookName",
        vb.book_code AS "BookCode",
        vb.cell_number AS "CellNumber",
        COUNT(*) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM sk_vending_books vb
    WHERE vb.vending_station_id = p_stationid
      AND (
            p_searchstring IS NULL
            OR p_searchstring = ''
            OR vb.book_name ILIKE '%' || p_searchstring || '%'
            OR vb.book_code ILIKE '%' || p_searchstring || '%'
          )
    ORDER BY vb.book_name
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_vendings_by_master_id_get(uuid, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_vendings_by_master_id_get(p_ownerid uuid, p_masterid integer) RETURNS TABLE("SafeKeeperID" uuid, "SafeKeeperName" text, "IsAllow" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF p_masterid IS NOT NULL THEN
        RETURN QUERY
        SELECT DISTINCT
            sk.id AS "SafeKeeperID",
            (l.name || ' / ' || s.name) AS "SafeKeeperName",
            EXISTS (
                SELECT 1 
                FROM sk_safekeepers_masters_rfid 
                WHERE master_rfid_id = p_masterid 
                  AND safe_keeper_id = sk.id
                LIMIT 1
            ) AS "IsAllow"
        FROM sk_safekeepers sk
        INNER JOIN mon_stations s ON s.id = sk.id
        INNER JOIN lib_libraries l ON l.id = s.library_id
        WHERE l.owner_id = p_ownerid AND s.station_type_id = 9
        ORDER BY "SafeKeeperName";
    ELSE
        RETURN QUERY
        SELECT DISTINCT
            sk.id AS "SafeKeeperID",
            (l.name || ' / ' || s.name) AS "SafeKeeperName",
            false AS "IsAllow"
        FROM sk_safekeepers sk
        INNER JOIN mon_stations s ON s.id = sk.id
        INNER JOIN lib_libraries l ON l.id = s.library_id
        WHERE l.owner_id = p_ownerid AND s.station_type_id = 9
        ORDER BY "SafeKeeperName";
    END IF;
END;
$$;


--
-- Name: web_vendings_get(uuid, uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_vendings_get(p_libraryid uuid, p_ownerid uuid) RETURNS TABLE("ID" uuid, "Name" text, "DateCreated" timestamp with time zone, "LastUpdate" timestamp with time zone, "IsOnline" boolean, "SN" text, "TypeID" integer, "TypeName" text, "LibraryID" uuid, "IsReaderOk" boolean, "IsCardReaderOk" boolean, "IsPrinterOk" boolean, "IsABISOk" boolean, "IsControllerOk" boolean, "IsConveyorOk" boolean)
    LANGUAGE plpgsql
    AS $$
BEGIN
  RETURN QUERY
  SELECT
    s.id AS "ID",
    s.name AS "Name",
    s.date_created AS "DateCreated",
    s.last_update AS "LastUpdate",
    (COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.last_update)) / 60, 3) < 2) AS "IsOnline",
    s.sn AS "SN",
    st.id AS "TypeID",
    st.name AS "TypeName",
    s.library_id AS "LibraryID",
    (COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part1)) / 60, 3) < 2) AS "IsReaderOk",
    (COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part2)) / 60, 3) < 2) AS "IsCardReaderOk",
    (COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part3)) / 60, 3) < 2) AS "IsPrinterOk",
    (COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part4)) / 60, 3) < 2) AS "IsABISOk",
    (COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part5)) / 60, 3) < 2) AS "IsControllerOk",
    (COALESCE(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - s.monitor_part6)) / 60, 3) < 2) AS "IsConveyorOk"
  FROM mon_stations s
  INNER JOIN mon_station_types st ON st.id = s.station_type_id
  INNER JOIN lib_libraries l ON l.id = s.library_id
  WHERE ((p_libraryid IS NOT NULL AND l.id = p_libraryid) 
         OR (p_libraryid IS NULL AND l.owner_id = p_ownerid))
    AND s.station_type_id = 9
  ORDER BY s.name;
END;
$$;


--
-- Name: web_vendings_history_get(uuid, uuid, timestamp with time zone, timestamp with time zone, text, integer, integer, integer, integer); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_vendings_history_get(p_libraryid uuid, p_ownerid uuid, p_datestart timestamp with time zone, p_dateend timestamp with time zone, p_searchstring text, p_sortid integer, p_sortdirection integer, p_rowsonpage integer, p_rowsoffset integer) RETURNS TABLE("ID" integer, "DateEvent" timestamp with time zone, "StationID" uuid, "StationName" text, "EventID" integer, "EventName" text, "Message" text, "UserCode" text, "UserName" text, "RowsCount" integer)
    LANGUAGE plpgsql
    AS $$
DECLARE
    event_ids INTEGER[] := ARRAY[8,9,10,11,13,14,23,25,26,49,50,51,52];
BEGIN
    p_datestart := p_datestart::DATE;
    p_dateend := p_dateend::DATE;

    RETURN QUERY
    SELECT 
        seh.id AS "ID",
        seh.date_event AS "DateEvent",
        seh.station_id AS "StationID",
        s.name AS "StationName",
        seh.event_id AS "EventID",
        e.name AS "EventName",
        seh.message AS "Message",
        seh.user_code AS "UserCode",
        COALESCE(r1.name, r2.name) AS "UserName",
        COUNT(1) OVER ()::INTEGER AS "RowsCount"  -- ← явное приведение к INTEGER
    FROM mon_stations_event_history seh
    INNER JOIN mon_stations s ON s.id = seh.station_id
    INNER JOIN mon_events e ON e.id = seh.event_id
    LEFT JOIN rdr_readers r1 ON seh.user_code = r1.abis_code
    LEFT JOIN rdr_readers r2 ON seh.user_code = r2.rfid_code
    WHERE (seh.date_event::DATE BETWEEN p_datestart AND p_dateend)
        AND (
            (p_libraryid IS NOT NULL AND s.library_id = p_libraryid) 
            OR (p_libraryid IS NULL AND s.library_id IN (
                SELECT id FROM lib_libraries WHERE owner_id = p_ownerid
            ))
        )
        AND (p_searchstring IS NULL OR p_searchstring = '' OR (
            s.name ILIKE '%' || p_searchstring || '%'
            OR e.name ILIKE '%' || p_searchstring || '%'
            OR seh.message ILIKE '%' || p_searchstring || '%'
            OR seh.user_code ILIKE '%' || p_searchstring || '%'
        ))
        AND seh.event_id = ANY(event_ids)
        AND s.station_type_id = 9
    ORDER BY
        CASE WHEN p_sortdirection = 0 AND p_sortid = 0 THEN seh.date_event END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 0 THEN seh.date_event END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 1 THEN s.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 1 THEN s.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 2 THEN e.name END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 2 THEN e.name END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 3 THEN seh.message END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 3 THEN seh.message END DESC NULLS LAST,
        CASE WHEN p_sortdirection = 0 AND p_sortid = 4 THEN seh.user_code END ASC NULLS LAST,
        CASE WHEN p_sortdirection = 1 AND p_sortid = 4 THEN seh.user_code END DESC NULLS LAST
    OFFSET p_rowsoffset
    LIMIT p_rowsonpage;
END;
$$;


--
-- Name: web_version_get(uuid); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.web_version_get(p_ownerid uuid) RETURNS TABLE(version integer)
    LANGUAGE plpgsql
    AS $$
BEGIN
    RETURN QUERY
    SELECT o.version
    FROM owners o
    WHERE o.id = p_ownerid
    LIMIT 1;
END;
$$;


SET default_tablespace = '';

SET default_table_access_method = heap;

--
-- Name: acs_direction_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.acs_direction_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: acs_entrances; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.acs_entrances (
    id integer NOT NULL,
    name text DEFAULT 'Новый проход'::text NOT NULL,
    library_id uuid NOT NULL
);


--
-- Name: acs_entrances_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.acs_entrances_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: acs_entrances_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.acs_entrances_id_seq OWNED BY public.acs_entrances.id;


--
-- Name: acs_events_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.acs_events_history (
    id integer NOT NULL,
    event_date timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    rfid_code text NOT NULL,
    client_name text,
    reader_id integer NOT NULL,
    reader_port_number integer NOT NULL
);


--
-- Name: acs_eventshistory_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.acs_eventshistory_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: acs_eventshistory_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.acs_eventshistory_id_seq OWNED BY public.acs_events_history.id;


--
-- Name: acs_reader_antennas; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.acs_reader_antennas (
    id integer NOT NULL,
    reader_port_number integer NOT NULL,
    name text DEFAULT 'Новая антенна'::text NOT NULL,
    zone_id integer NOT NULL,
    reader_id integer NOT NULL
);


--
-- Name: acs_readerantennas_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.acs_readerantennas_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: acs_readerantennas_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.acs_readerantennas_id_seq OWNED BY public.acs_reader_antennas.id;


--
-- Name: acs_readers; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.acs_readers (
    id integer NOT NULL,
    name text DEFAULT 'Новый ридер'::text NOT NULL,
    ip_address text,
    port integer,
    entrance_id integer NOT NULL,
    reader_type_id integer NOT NULL,
    last_update timestamp with time zone
);


--
-- Name: acs_readers_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.acs_readers_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: acs_readers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.acs_readers_id_seq OWNED BY public.acs_readers.id;


--
-- Name: acs_readers_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.acs_readers_types (
    id integer NOT NULL,
    name text NOT NULL,
    ports_count integer DEFAULT 1 NOT NULL
);


--
-- Name: acs_zones; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.acs_zones (
    id integer NOT NULL,
    name text DEFAULT 'Новая зона'::text NOT NULL,
    entrance_id integer NOT NULL,
    direction_type_id integer NOT NULL
);


--
-- Name: acs_zones_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.acs_zones_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: acs_zones_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.acs_zones_id_seq OWNED BY public.acs_zones.id;


--
-- Name: attach_files; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.attach_files (
    uid uuid DEFAULT gen_random_uuid() NOT NULL,
    file_name text NOT NULL,
    file_path text NOT NULL,
    file_size bigint NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
    updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
);


--
-- Name: TABLE attach_files; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON TABLE public.attach_files IS 'File attachments storage';


--
-- Name: COLUMN attach_files.uid; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.attach_files.uid IS 'Unique identifier for the file';


--
-- Name: COLUMN attach_files.file_name; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.attach_files.file_name IS 'Original file name';


--
-- Name: COLUMN attach_files.file_path; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.attach_files.file_path IS 'File storage path';


--
-- Name: COLUMN attach_files.file_size; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.attach_files.file_size IS 'File size in bytes';


--
-- Name: COLUMN attach_files.date_created; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.attach_files.date_created IS 'When the file was uploaded';


--
-- Name: auth_api_keys; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_api_keys (
    id integer NOT NULL,
    login_id integer NOT NULL,
    key uuid,
    created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
    updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
);


--
-- Name: TABLE auth_api_keys; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON TABLE public.auth_api_keys IS 'API keys for authentication';


--
-- Name: COLUMN auth_api_keys.id; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.auth_api_keys.id IS 'Primary key identifier';


--
-- Name: COLUMN auth_api_keys.login_id; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.auth_api_keys.login_id IS 'Reference to auth_logins table';


--
-- Name: COLUMN auth_api_keys.key; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.auth_api_keys.key IS 'Unique API key identifier';


--
-- Name: auth_api_keys_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.auth_api_keys_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: auth_api_keys_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.auth_api_keys_id_seq OWNED BY public.auth_api_keys.id;


--
-- Name: auth_login_log_warnings; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_login_log_warnings (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    login text NOT NULL,
    summary_hash text NOT NULL,
    created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
);


--
-- Name: TABLE auth_login_log_warnings; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON TABLE public.auth_login_log_warnings IS 'Log warnings for login attempts';


--
-- Name: COLUMN auth_login_log_warnings.id; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.auth_login_log_warnings.id IS 'Primary key identifier';


--
-- Name: COLUMN auth_login_log_warnings.date_created; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.auth_login_log_warnings.date_created IS 'When the warning was logged';


--
-- Name: COLUMN auth_login_log_warnings.login; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.auth_login_log_warnings.login IS 'Login name that triggered warning';


--
-- Name: COLUMN auth_login_log_warnings.summary_hash; Type: COMMENT; Schema: public; Owner: -
--

COMMENT ON COLUMN public.auth_login_log_warnings.summary_hash IS 'Hash summary for duplicate detection';


--
-- Name: auth_login_log_warnings_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.auth_login_log_warnings_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: auth_login_log_warnings_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.auth_login_log_warnings_id_seq OWNED BY public.auth_login_log_warnings.id;


--
-- Name: auth_login_logs; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_login_logs (
    id bigint NOT NULL,
    login text NOT NULL,
    login_id integer,
    password text,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    ip text NOT NULL,
    login_from_url text NOT NULL,
    user_agent text NOT NULL,
    fingerprint text NOT NULL,
    is_success boolean DEFAULT false NOT NULL,
    summary_hash text NOT NULL,
    created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
);


--
-- Name: auth_login_logs_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.auth_login_logs_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: auth_login_logs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.auth_login_logs_id_seq OWNED BY public.auth_login_logs.id;


--
-- Name: auth_login_roles_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.auth_login_roles_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: auth_login_roles; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_login_roles (
    id integer DEFAULT nextval('public.auth_login_roles_id_seq'::regclass) NOT NULL,
    date_created timestamp with time zone DEFAULT now() NOT NULL,
    login_id integer NOT NULL,
    role_type_id integer DEFAULT 1 NOT NULL,
    role_sub_id integer DEFAULT '-1'::integer NOT NULL
);


--
-- Name: auth_login_roles_ext; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_login_roles_ext (
    login_id integer NOT NULL,
    is_view_monitoring boolean DEFAULT true NOT NULL,
    is_view_eas boolean DEFAULT true NOT NULL,
    is_view_safe_keepers boolean DEFAULT true NOT NULL,
    is_view_stations boolean DEFAULT true NOT NULL,
    is_view_smart_shelves boolean DEFAULT true NOT NULL,
    is_view_desktop_readers boolean DEFAULT true NOT NULL,
    is_view_acs boolean DEFAULT true NOT NULL,
    is_view_librarian boolean DEFAULT true NOT NULL,
    is_view_book_keeping boolean DEFAULT true NOT NULL,
    is_view_reports boolean DEFAULT true NOT NULL,
    is_view_kpi_analytic boolean DEFAULT true NOT NULL,
    is_view_face_detect boolean DEFAULT true NOT NULL,
    is_view_users boolean DEFAULT true NOT NULL,
    is_view_settings boolean DEFAULT true NOT NULL,
    is_view_vendings boolean DEFAULT true NOT NULL,
    is_view_smart_assistent boolean DEFAULT true NOT NULL,
    is_view_libraries boolean DEFAULT true NOT NULL
);


--
-- Name: auth_logins; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_logins (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    login text NOT NULL,
    first_name text,
    second_name text,
    last_name text,
    pass_md5 text NOT NULL,
    pass_salt text NOT NULL,
    expired timestamp with time zone NOT NULL,
    stamp bytea NOT NULL,
    owner_id uuid NOT NULL,
    _pass text,
    token_life_minutes integer DEFAULT 0 NOT NULL,
    is_deleted boolean DEFAULT false NOT NULL,
    is_internal_access_enabled boolean DEFAULT false NOT NULL
);


--
-- Name: auth_logins_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.auth_logins_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: auth_logins_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.auth_logins_id_seq OWNED BY public.auth_logins.id;


--
-- Name: auth_role_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_role_types (
    id integer NOT NULL,
    name text NOT NULL,
    has_sub_role boolean NOT NULL
);


--
-- Name: auth_sessions; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.auth_sessions (
    id bigint NOT NULL,
    login_id integer NOT NULL,
    date_created timestamp with time zone NOT NULL,
    date_updated timestamp with time zone NOT NULL,
    user_agent text NOT NULL,
    fingerprint text NOT NULL,
    ip text NOT NULL,
    refresh_token uuid NOT NULL,
    refresh_token_expiration_time_minutes integer NOT NULL
);


--
-- Name: auth_sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.auth_sessions_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: auth_sessions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.auth_sessions_id_seq OWNED BY public.auth_sessions.id;


--
-- Name: bk_backgrounds; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_backgrounds (
    id integer NOT NULL,
    background bytea NOT NULL,
    opacity double precision DEFAULT 1 NOT NULL,
    library_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: bk_backgrounds_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.bk_backgrounds_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: bk_backgrounds_id_seq1; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.bk_backgrounds_id_seq1
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: bk_backgrounds_id_seq1; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.bk_backgrounds_id_seq1 OWNED BY public.bk_backgrounds.id;


--
-- Name: bk_book_case_sections; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_book_case_sections (
    id integer NOT NULL,
    name text NOT NULL,
    library_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: bk_book_case_sections_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.bk_book_case_sections_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: bk_book_case_sections_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.bk_book_case_sections_id_seq OWNED BY public.bk_book_case_sections.id;


--
-- Name: bk_book_cases; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_book_cases (
    id integer NOT NULL,
    name text NOT NULL,
    library_id uuid NOT NULL,
    is_deleted boolean DEFAULT false NOT NULL,
    coordinates text DEFAULT '1;1;0;1'::character varying NOT NULL,
    book_case_section_id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: bk_book_cases_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.bk_book_cases_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: bk_book_cases_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.bk_book_cases_id_seq OWNED BY public.bk_book_cases.id;


--
-- Name: bk_book_cells; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_book_cells (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    number integer NOT NULL,
    book_shelf_id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: bk_book_inv_states; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_book_inv_states (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: bk_book_shelves; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_book_shelves (
    id integer NOT NULL,
    name text NOT NULL,
    book_case_id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: bk_book_shelves_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.bk_book_shelves_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: bk_book_shelves_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.bk_book_shelves_id_seq OWNED BY public.bk_book_shelves.id;


--
-- Name: bk_book_states; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_book_states (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: bk_books; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_books (
    book_id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    state_id integer NOT NULL,
    cell_id uuid NOT NULL
);


--
-- Name: bk_books_cache; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_books_cache (
    book_code text NOT NULL,
    library_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    date_last_update timestamp with time zone NOT NULL,
    date_checked timestamp with time zone,
    is_checked boolean DEFAULT false NOT NULL
);


--
-- Name: bk_environment; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_environment (
    id integer NOT NULL,
    color_id integer NOT NULL,
    library_id uuid NOT NULL,
    coordinates text DEFAULT '0;0;0'::character varying NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: bk_environment_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.bk_environment_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: bk_environment_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.bk_environment_id_seq OWNED BY public.bk_environment.id;


--
-- Name: bk_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.bk_history (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    book_id integer NOT NULL,
    inv_state_id integer NOT NULL,
    executor_id integer,
    book_cell_id uuid NOT NULL
);


--
-- Name: bk_history_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.bk_history_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: bk_history_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.bk_history_id_seq OWNED BY public.bk_history.id;


--
-- Name: call_logs; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.call_logs (
    id uuid NOT NULL,
    method_name text NOT NULL,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    parameters_json jsonb,
    exception_message text,
    exception_stack_trace text
);


--
-- Name: dir_colors; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.dir_colors (
    id integer NOT NULL,
    color_html text NOT NULL
);


--
-- Name: dir_days; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.dir_days (
    day_value timestamp with time zone NOT NULL
);


--
-- Name: dir_hours_day; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.dir_hours_day (
    hour_value integer NOT NULL
);


--
-- Name: fd_device_cameras; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.fd_device_cameras (
    id integer NOT NULL,
    name text NOT NULL,
    device_id integer NOT NULL,
    channel_no integer NOT NULL,
    login text DEFAULT 'admin'::character varying NOT NULL,
    password text DEFAULT 'admin'::character varying NOT NULL,
    is_active boolean DEFAULT true NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    is_usb boolean DEFAULT false NOT NULL,
    ip text NOT NULL,
    brightness integer DEFAULT 128 NOT NULL,
    contrast integer DEFAULT 128 NOT NULL,
    sharpness integer DEFAULT 128 NOT NULL
);


--
-- Name: fd_device_cameras_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.fd_device_cameras_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: fd_device_cameras_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.fd_device_cameras_id_seq OWNED BY public.fd_device_cameras.id;


--
-- Name: fd_devices; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.fd_devices (
    id integer NOT NULL,
    hardware_id text NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    ip text NOT NULL,
    port integer NOT NULL,
    login text NOT NULL,
    password text NOT NULL,
    date_last_online timestamp with time zone,
    library_id uuid,
    last_processed_id integer DEFAULT 0 NOT NULL
);


--
-- Name: fd_devices_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.fd_devices_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: fd_devices_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.fd_devices_id_seq OWNED BY public.fd_devices.id;


--
-- Name: fd_logs; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.fd_logs (
    id integer NOT NULL,
    device_id integer NOT NULL,
    device_camera_id integer,
    log_type integer NOT NULL,
    date_created timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    message text NOT NULL
);


--
-- Name: fd_logs_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.fd_logs_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: fd_logs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.fd_logs_id_seq OWNED BY public.fd_logs.id;


--
-- Name: fd_visitors_full_cadre; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.fd_visitors_full_cadre (
    visitor_photo_id integer NOT NULL,
    full_cadre bytea NOT NULL,
    full_cadre2 bytea
);


--
-- Name: fd_visitors_photo; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.fd_visitors_photo (
    id integer NOT NULL,
    camera_id integer NOT NULL,
    reader_id integer,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    photo bytea NOT NULL
);


--
-- Name: fd_visitors_photo_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.fd_visitors_photo_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: fd_visitors_photo_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.fd_visitors_photo_id_seq OWNED BY public.fd_visitors_photo.id;


--
-- Name: fd_visitors_values; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.fd_visitors_values (
    visitor_photo_id integer NOT NULL,
    age integer NOT NULL,
    attraction integer NOT NULL,
    beard integer NOT NULL,
    emotion integer NOT NULL,
    eyes integer NOT NULL,
    glasses integer NOT NULL,
    mask integer NOT NULL,
    mouth integer NOT NULL,
    similarity integer NOT NULL,
    race integer NOT NULL,
    sex integer NOT NULL
);


--
-- Name: gpt_languages; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.gpt_languages (
    id integer NOT NULL,
    name text NOT NULL,
    code text NOT NULL
);


--
-- Name: gpt_languages_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.gpt_languages_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: gpt_languages_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.gpt_languages_id_seq OWNED BY public.gpt_languages.id;


--
-- Name: gpt_voice_assistant_settings; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.gpt_voice_assistant_settings (
    station_id uuid NOT NULL,
    is_enabled boolean NOT NULL,
    voice_type_id integer NOT NULL,
    language_id integer NOT NULL,
    last_updated timestamp with time zone,
    prompt text,
    knowledge_base text,
    stop_words text,
    api_key text,
    folder_id text,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: gpt_voice_interactions; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.gpt_voice_interactions (
    id bigint NOT NULL,
    station_id uuid NOT NULL,
    library_id uuid NOT NULL,
    request_text text,
    response_text text,
    session_duration double precision DEFAULT 0 NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    is_template_used boolean DEFAULT false NOT NULL,
    template_id integer,
    rfid_code text,
    reader_id integer
);


--
-- Name: gpt_voice_interactions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.gpt_voice_interactions_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: gpt_voice_interactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.gpt_voice_interactions_id_seq OWNED BY public.gpt_voice_interactions.id;


--
-- Name: gpt_voice_response_templates; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.gpt_voice_response_templates (
    id integer NOT NULL,
    library_id uuid NOT NULL,
    question_text text NOT NULL,
    answer_text text NOT NULL,
    is_active boolean DEFAULT true NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    date_updated timestamp with time zone,
    font_size integer DEFAULT 16 NOT NULL,
    font_color text DEFAULT '#000000'::character varying NOT NULL,
    border_color text DEFAULT '#000000'::character varying NOT NULL
);


--
-- Name: gpt_voice_response_templates_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.gpt_voice_response_templates_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: gpt_voice_response_templates_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.gpt_voice_response_templates_id_seq OWNED BY public.gpt_voice_response_templates.id;


--
-- Name: gpt_voice_template_stations; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.gpt_voice_template_stations (
    template_id integer NOT NULL,
    station_id uuid NOT NULL,
    is_active boolean DEFAULT true NOT NULL
);


--
-- Name: gpt_voice_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.gpt_voice_types (
    id integer NOT NULL,
    name text NOT NULL,
    description text
);


--
-- Name: gpt_voice_types_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.gpt_voice_types_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: gpt_voice_types_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.gpt_voice_types_id_seq OWNED BY public.gpt_voice_types.id;


--
-- Name: internal; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.internal (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    payment_date timestamp with time zone,
    payment_demo_mode integer,
    last_device_data_id bigint DEFAULT 0 NOT NULL,
    last_gate_events_id bigint DEFAULT 0 NOT NULL,
    last_gate_alarms_id bigint DEFAULT 0 NOT NULL,
    last_gate_counters_id bigint DEFAULT 0 NOT NULL,
    last_station_events_id bigint DEFAULT 0 NOT NULL,
    date_bp timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
    name_text text,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    comment text,
    firm_id integer,
    is_reader_for_internal boolean DEFAULT false NOT NULL
);


--
-- Name: kpi_service_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.kpi_service_history (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    login_id integer NOT NULL,
    reader_id integer,
    service_id integer NOT NULL
);


--
-- Name: kpi_service_history_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.kpi_service_history_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: kpi_service_history_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.kpi_service_history_id_seq OWNED BY public.kpi_service_history.id;


--
-- Name: kpi_services; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.kpi_services (
    id integer NOT NULL,
    name text NOT NULL,
    is_auth_required boolean DEFAULT true NOT NULL,
    type_id integer,
    is_deleted boolean DEFAULT false NOT NULL,
    library_id uuid NOT NULL
);


--
-- Name: kpi_services_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.kpi_services_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: kpi_services_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.kpi_services_id_seq OWNED BY public.kpi_services.id;


--
-- Name: kpi_services_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.kpi_services_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: kpi_services_types_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.kpi_services_types_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: kpi_services_types_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.kpi_services_types_id_seq OWNED BY public.kpi_services_types.id;


--
-- Name: lib_book_user_ratings; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.lib_book_user_ratings (
    id integer NOT NULL,
    book_id integer NOT NULL,
    reader_id integer NOT NULL,
    rate integer,
    volume_complete integer,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: lib_book_user_ratings_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.lib_book_user_ratings_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: lib_book_user_ratings_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.lib_book_user_ratings_id_seq OWNED BY public.lib_book_user_ratings.id;


--
-- Name: lib_books; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.lib_books (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    book_code text NOT NULL,
    book_name text,
    authors_name text,
    date_updated timestamp with time zone,
    is_book integer DEFAULT 1 NOT NULL,
    book_place text,
    library_id uuid NOT NULL,
    description text,
    is_special boolean DEFAULT false NOT NULL,
    state_id integer
);


--
-- Name: lib_books_covers; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.lib_books_covers (
    book_id integer NOT NULL,
    cover bytea
);


--
-- Name: lib_books_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.lib_books_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: lib_books_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.lib_books_id_seq OWNED BY public.lib_books.id;


--
-- Name: lib_libraries; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.lib_libraries (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    name text DEFAULT 'Новая библиотека'::text NOT NULL,
    fullname text,
    manager text,
    phone text,
    address text,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    tag_service_count integer DEFAULT 0 NOT NULL,
    tag_service_count_hash text,
    owner_id uuid NOT NULL,
    ts integer DEFAULT 0 NOT NULL,
    firm_id integer,
    comment text
);


--
-- Name: log_common; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_common (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_easybookrfid; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_easybookrfid (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_gates; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_gates (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_login; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_login (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_patrons; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_patrons (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_safekeepers; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_safekeepers (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_smartshelves; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_smartshelves (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_stat; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_stat (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_stations; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_stations (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_unknown; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_unknown (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_users; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_users (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: log_vendings; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.log_vendings (
    id uuid NOT NULL,
    http_method character varying(10) NOT NULL,
    request_url character varying(2000) NOT NULL,
    request_body_json text,
    response_body_json text,
    start_time timestamp with time zone NOT NULL,
    end_time timestamp with time zone,
    duration_ms bigint,
    ip_address character varying(45),
    controller_name character varying(100) NOT NULL,
    action_name character varying(100) NOT NULL
);


--
-- Name: main_modules; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.main_modules (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: mon_book_shelves_stat; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_book_shelves_stat (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    station_id uuid NOT NULL,
    book_id integer NOT NULL,
    is_online boolean,
    count_takes integer DEFAULT 0 NOT NULL
);


--
-- Name: mon_book_shelves_stat_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_book_shelves_stat_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_book_shelves_stat_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_book_shelves_stat_id_seq OWNED BY public.mon_book_shelves_stat.id;


--
-- Name: mon_device_cfg_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_device_cfg_types (
    id integer NOT NULL,
    name text
);


--
-- Name: mon_device_data; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_device_data (
    id integer NOT NULL,
    date_value timestamp with time zone NOT NULL,
    hour_value integer NOT NULL,
    device_id uuid NOT NULL,
    soft_online_count integer DEFAULT 0 NOT NULL,
    device_ok_count integer DEFAULT 0 NOT NULL,
    device_error_count integer DEFAULT 0 NOT NULL,
    ip_address text
);


--
-- Name: mon_device_data_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_device_data_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_device_data_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_device_data_id_seq OWNED BY public.mon_device_data.id;


--
-- Name: mon_device_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_device_types (
    id integer NOT NULL,
    name text,
    device_cfg_type_id integer
);


--
-- Name: mon_devices; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_devices (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    name text DEFAULT 'Новое рабочее место'::character varying NOT NULL,
    type_id integer NOT NULL,
    last_update timestamp with time zone,
    cfg text,
    cfg2 text,
    library_id uuid NOT NULL
);


--
-- Name: mon_event_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_event_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: mon_events; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_events (
    id integer NOT NULL,
    name text NOT NULL,
    event_type_id integer NOT NULL,
    is_single_event integer DEFAULT 1 NOT NULL
);


--
-- Name: mon_gate_cfg_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_gate_cfg_types (
    id integer NOT NULL,
    name text NOT NULL,
    type_id integer NOT NULL
);


--
-- Name: mon_gate_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_gate_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: mon_gates; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_gates (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    name text,
    ip text,
    port integer,
    type_id integer NOT NULL,
    cfg_type_id integer NOT NULL,
    last_update timestamp with time zone,
    library_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: mon_gates_counters; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_gates_counters (
    id integer NOT NULL,
    gate_id uuid NOT NULL,
    date_value timestamp with time zone NOT NULL,
    value_in integer,
    value_out integer
);


--
-- Name: mon_gates_counters_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_gates_counters_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_gates_counters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_gates_counters_id_seq OWNED BY public.mon_gates_counters.id;


--
-- Name: mon_gates_eas; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_gates_eas (
    id integer NOT NULL,
    gate_id uuid NOT NULL,
    date_value timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    uid text
);


--
-- Name: mon_gates_eas_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_gates_eas_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_gates_eas_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_gates_eas_id_seq OWNED BY public.mon_gates_eas.id;


--
-- Name: mon_gates_event_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_gates_event_history (
    id integer NOT NULL,
    date_value timestamp with time zone NOT NULL,
    gate_id uuid NOT NULL,
    event_id integer NOT NULL,
    comment text NOT NULL,
    count integer DEFAULT 0 NOT NULL
);


--
-- Name: mon_gates_event_history_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_gates_event_history_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_gates_event_history_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_gates_event_history_id_seq OWNED BY public.mon_gates_event_history.id;


--
-- Name: mon_mono_gate_count_devices; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_mono_gate_count_devices (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    name text,
    ip text NOT NULL,
    port integer DEFAULT 0 NOT NULL,
    date_last_online timestamp with time zone,
    gate_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: mon_service_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_service_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: mon_services; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_services (
    id integer NOT NULL,
    type_id integer NOT NULL,
    date_last_online timestamp with time zone,
    version text,
    library_id uuid NOT NULL
);


--
-- Name: mon_services_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_services_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_services_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_services_id_seq OWNED BY public.mon_services.id;


--
-- Name: mon_station_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_station_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: mon_stations; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_stations (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    name text,
    station_type_id integer NOT NULL,
    last_update timestamp with time zone,
    sn text,
    device_camera_id integer,
    library_id uuid NOT NULL,
    monitor_part1 timestamp with time zone,
    monitor_part2 timestamp with time zone,
    monitor_part3 timestamp with time zone,
    monitor_part4 timestamp with time zone,
    monitor_part5 timestamp with time zone,
    monitor_part6 timestamp with time zone,
    monitor_part7 timestamp with time zone,
    monitor_part8 timestamp with time zone,
    monitor_part9 timestamp with time zone,
    coordinates text,
    ip text,
    monitor_part10 timestamp with time zone,
    monitor_part11 timestamp with time zone
);


--
-- Name: mon_stations_banners; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_stations_banners (
    id integer NOT NULL,
    name text DEFAULT 'Новый баннер'::character varying,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    period_from timestamp with time zone,
    period_to timestamp with time zone,
    "interval" integer DEFAULT 0 NOT NULL,
    image bytea,
    is_deleted boolean DEFAULT false NOT NULL,
    login_id integer NOT NULL,
    is_enabled boolean DEFAULT true NOT NULL
);


--
-- Name: mon_stations_banners_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_stations_banners_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_stations_banners_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_stations_banners_id_seq OWNED BY public.mon_stations_banners.id;


--
-- Name: mon_stations_banners_tasks; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_stations_banners_tasks (
    banner_id integer NOT NULL,
    station_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    owner_id uuid NOT NULL
);


--
-- Name: mon_stations_config; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_stations_config (
    station_id uuid NOT NULL,
    config text,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: mon_stations_event_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_stations_event_history (
    id integer NOT NULL,
    station_id uuid NOT NULL,
    date_event timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    event_id integer NOT NULL,
    message text,
    count integer,
    user_code text,
    pair_id integer
);


--
-- Name: mon_stations_event_history_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_stations_event_history_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_stations_event_history_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_stations_event_history_id_seq OWNED BY public.mon_stations_event_history.id;


--
-- Name: mon_stations_monitoring; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_stations_monitoring (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    station_id uuid NOT NULL
);


--
-- Name: mon_stations_monitoring_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.mon_stations_monitoring_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: mon_stations_monitoring_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.mon_stations_monitoring_id_seq OWNED BY public.mon_stations_monitoring.id;


--
-- Name: mon_stations_states_update_log; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.mon_stations_states_update_log (
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    sn text NOT NULL
);


--
-- Name: monitor_device_data; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_device_data (
    id_key uuid DEFAULT gen_random_uuid() NOT NULL,
    id integer NOT NULL,
    internal_guid uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    device_id uuid NOT NULL,
    date_value timestamp with time zone NOT NULL,
    hour_value integer NOT NULL,
    soft_online_count integer DEFAULT 0 NOT NULL,
    device_ok_count integer DEFAULT 0 NOT NULL,
    device_error_count integer DEFAULT 0 NOT NULL
);


--
-- Name: monitor_device_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_device_types (
    id integer NOT NULL,
    name text,
    device_cfg_type_id integer
);


--
-- Name: monitor_devices; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_devices (
    device_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    name text,
    type_id integer,
    last_id integer,
    library_id uuid NOT NULL
);


--
-- Name: monitor_gates; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_gates (
    id uuid NOT NULL,
    name text,
    ip text,
    port integer,
    type_id integer,
    last_update timestamp with time zone,
    library_id uuid NOT NULL
);


--
-- Name: monitor_gates_counters; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_gates_counters (
    id_key uuid DEFAULT gen_random_uuid() NOT NULL,
    internal_guid uuid NOT NULL,
    id bigint NOT NULL,
    gate_id uuid NOT NULL,
    date_value timestamp with time zone NOT NULL,
    value_in integer,
    value_out integer
);


--
-- Name: monitor_gates_eas; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_gates_eas (
    id_key uuid DEFAULT gen_random_uuid() NOT NULL,
    id bigint NOT NULL,
    internal_guid uuid NOT NULL,
    gate_id uuid NOT NULL,
    date_value timestamp with time zone NOT NULL,
    uid text
);


--
-- Name: monitor_gates_event_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_gates_event_history (
    id_key uuid DEFAULT gen_random_uuid() NOT NULL,
    id bigint NOT NULL,
    internal_guid uuid NOT NULL,
    date_value timestamp with time zone NOT NULL,
    gate_id uuid NOT NULL,
    event_id integer NOT NULL,
    comment text NOT NULL,
    count integer NOT NULL
);


--
-- Name: monitor_internals; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_internals (
    guid uuid NOT NULL,
    custom_name text,
    firm_id integer,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    payment_date timestamp with time zone,
    payment_demo_mode integer,
    ip text,
    comment text
);


--
-- Name: monitor_internals_snapshot; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_internals_snapshot (
    guid uuid DEFAULT gen_random_uuid() NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    payment_date timestamp with time zone,
    payment_demo_mode integer,
    ip text,
    comment text
);


--
-- Name: monitor_libraries; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_libraries (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    owner_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    bp_unit_id integer,
    name text,
    full_name text,
    manager text,
    phone text,
    address text,
    tag_service_count integer,
    tag_service_count_hash text,
    firm_id integer,
    comment text
);


--
-- Name: monitor_libraries_snapshot; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_libraries_snapshot (
    id integer NOT NULL,
    internal_guid uuid NOT NULL,
    guid uuid NOT NULL,
    name text NOT NULL,
    full_name text,
    manager text,
    phone text,
    address text,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    tag_service_count integer NOT NULL,
    tag_service_count_hash text
);


--
-- Name: monitor_libraries_snapshot_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.monitor_libraries_snapshot_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: monitor_libraries_snapshot_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.monitor_libraries_snapshot_id_seq OWNED BY public.monitor_libraries_snapshot.id;


--
-- Name: monitor_owners; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_owners (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    internal_guid uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    name_original text,
    bp_owner_id integer,
    version integer,
    firm_id integer,
    comment text
);


--
-- Name: monitor_owners_snapshot; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_owners_snapshot (
    id uuid NOT NULL,
    internal_guid uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    name text NOT NULL,
    version integer NOT NULL
);


--
-- Name: monitor_services_snapshot; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_services_snapshot (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    id_local integer NOT NULL,
    internal_guid uuid NOT NULL,
    type_id integer NOT NULL,
    version text
);


--
-- Name: monitor_stations; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_stations (
    id uuid NOT NULL,
    date_created timestamp with time zone NOT NULL,
    name text,
    station_type_id integer NOT NULL,
    last_update timestamp with time zone,
    sn text,
    library_id uuid NOT NULL
);


--
-- Name: monitor_stations_event_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.monitor_stations_event_history (
    id bigint NOT NULL,
    id_key uuid DEFAULT gen_random_uuid() NOT NULL,
    internal_guid uuid NOT NULL,
    station_id uuid NOT NULL,
    date_event timestamp with time zone NOT NULL,
    event_id integer NOT NULL,
    message text,
    count integer,
    user_code text,
    pair_id integer
);


--
-- Name: owners; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.owners (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    name text NOT NULL,
    address text,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    version integer DEFAULT 0 NOT NULL,
    ts1 text,
    internal_id uuid NOT NULL,
    firm_id integer,
    is_use_ekp boolean DEFAULT true NOT NULL
);


--
-- Name: prop_ais; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.prop_ais (
    login_id integer NOT NULL,
    server_address text,
    server_port integer,
    server_login text,
    server_password text,
    days_to_return_count integer DEFAULT 14 NOT NULL,
    books_db text,
    location_code text,
    institution_id text,
    is_age_control_enabled boolean DEFAULT false NOT NULL,
    is_only_one_prolong boolean DEFAULT false NOT NULL,
    is_check_debts boolean DEFAULT false NOT NULL,
    is_allow_take_if_debts boolean DEFAULT false NOT NULL,
    is_allow_return_if_debts boolean DEFAULT false NOT NULL,
    is_auth_required boolean DEFAULT false NOT NULL,
    is_use_file_log boolean DEFAULT false NOT NULL,
    is_use_sip2 boolean DEFAULT false NOT NULL
);


--
-- Name: prop_common; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.prop_common (
    login_id integer NOT NULL,
    is_use_send_char boolean DEFAULT false NOT NULL,
    is_use_eas_alarms boolean DEFAULT false NOT NULL,
    is_ignore_dm_card_type boolean DEFAULT false NOT NULL,
    receipt_width double precision DEFAULT 0,
    is_use_eas boolean DEFAULT false NOT NULL
);


--
-- Name: prop_email; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.prop_email (
    login_id integer NOT NULL,
    is_use_email_sender boolean DEFAULT false NOT NULL,
    smtp_server_address text,
    smtp_server_port integer,
    sender_address text,
    is_use_ssl boolean DEFAULT false NOT NULL,
    sender_login text,
    sender_password text,
    email_subject text
);


--
-- Name: prop_face_detect; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.prop_face_detect (
    login_id integer NOT NULL,
    camera_id integer,
    camera_address text,
    registrator_address text,
    registrator_port integer,
    camera_channel integer,
    registrator_login text
);


--
-- Name: prop_proxy_server; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.prop_proxy_server (
    login_id integer NOT NULL,
    is_use_proxy boolean,
    proxy_address text,
    proxy_port integer,
    is_auth_required boolean,
    proxy_login text,
    proxy_password text
);


--
-- Name: rdr_reader_photos; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.rdr_reader_photos (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    reader_id integer NOT NULL,
    photo bytea,
    is_deleted boolean DEFAULT false,
    photo2 bytea
);


--
-- Name: rdr_reader_photos_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.rdr_reader_photos_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: rdr_reader_photos_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.rdr_reader_photos_id_seq OWNED BY public.rdr_reader_photos.id;


--
-- Name: rdr_readers; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.rdr_readers (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    name text,
    abis_code text,
    rfid_code text,
    serial_number text,
    email text,
    lang_id integer,
    fontsize_id integer,
    is_rfid_code_valid boolean DEFAULT true NOT NULL,
    owner_id uuid,
    comment text
);


--
-- Name: rdr_readers_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.rdr_readers_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: rdr_readers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.rdr_readers_id_seq OWNED BY public.rdr_readers.id;


--
-- Name: sk_event_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_event_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: sk_history; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_history (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    cell_number integer NOT NULL,
    safe_keeper_id uuid NOT NULL,
    event_type_id integer NOT NULL,
    reader_rfid text NOT NULL,
    reader_fio text
);


--
-- Name: sk_history_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_history_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_history_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_history_id_seq OWNED BY public.sk_history.id;


--
-- Name: sk_history_master; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_history_master (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    cell_number integer,
    safe_keeper_id uuid NOT NULL,
    event_type_id integer NOT NULL,
    master_rfid_id integer NOT NULL,
    master_rfid_fio text NOT NULL
);


--
-- Name: sk_history_master_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_history_master_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_history_master_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_history_master_id_seq OWNED BY public.sk_history_master.id;


--
-- Name: sk_masters_rfid; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_masters_rfid (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    rfid text NOT NULL,
    last_name text,
    first_name text,
    second_name text,
    is_deleted boolean DEFAULT false NOT NULL,
    owner_id uuid NOT NULL
);


--
-- Name: sk_masters_rfid_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_masters_rfid_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_masters_rfid_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_masters_rfid_id_seq OWNED BY public.sk_masters_rfid.id;


--
-- Name: sk_order_body; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_order_body (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    book_rfid text,
    book_info text,
    is_verified boolean DEFAULT false NOT NULL,
    order_id integer NOT NULL,
    date_verified timestamp with time zone,
    is_processed boolean DEFAULT false NOT NULL,
    date_processed timestamp with time zone,
    modifier_id integer
);


--
-- Name: sk_order_body_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_order_body_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_order_body_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_order_body_id_seq OWNED BY public.sk_order_body.id;


--
-- Name: sk_order_states; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_order_states (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: sk_orders; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_orders (
    id integer NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    number text DEFAULT '0'::character varying NOT NULL,
    cell_number integer,
    reader_rfid text,
    state_id integer DEFAULT 1 NOT NULL,
    safe_keeper_id uuid NOT NULL,
    reader_fio text,
    login_id integer
);


--
-- Name: sk_orders_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_orders_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_orders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_orders_id_seq OWNED BY public.sk_orders.id;


--
-- Name: sk_returns; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_returns (
    id integer NOT NULL,
    station_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    date_closed timestamp with time zone,
    cell_number integer NOT NULL,
    patron_id integer NOT NULL,
    master_id integer
);


--
-- Name: sk_returns_body; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_returns_body (
    id integer NOT NULL,
    book_id text NOT NULL,
    return_id integer NOT NULL
);


--
-- Name: sk_returns_body_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_returns_body_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_returns_body_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_returns_body_id_seq OWNED BY public.sk_returns_body.id;


--
-- Name: sk_returns_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_returns_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_returns_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_returns_id_seq OWNED BY public.sk_returns.id;


--
-- Name: sk_safekeeper_types; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_safekeeper_types (
    id integer NOT NULL,
    name text NOT NULL
);


--
-- Name: sk_safekeepers; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_safekeepers (
    id uuid DEFAULT gen_random_uuid() NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    cells_count integer DEFAULT 3 NOT NULL,
    cells_state bigint DEFAULT 0,
    ip_address text,
    is_deleted boolean DEFAULT false NOT NULL,
    safe_keeper_type_id integer DEFAULT 2 NOT NULL,
    cell_shift integer DEFAULT 0 NOT NULL,
    parent_id uuid,
    controller_ip text,
    is_new_keeper boolean DEFAULT true NOT NULL,
    storage_time integer DEFAULT 0 NOT NULL
);


--
-- Name: sk_safekeepers_masters_rfid; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_safekeepers_masters_rfid (
    master_rfid_id integer NOT NULL,
    safe_keeper_id uuid NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);


--
-- Name: sk_storage_cells; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_storage_cells (
    id integer NOT NULL,
    reader_rfid text NOT NULL,
    cell_no integer NOT NULL,
    safe_keeper_id uuid NOT NULL,
    date_open timestamp with time zone NOT NULL,
    date_close timestamp with time zone,
    reader_fio text
);


--
-- Name: sk_storage_cells_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_storage_cells_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_storage_cells_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_storage_cells_id_seq OWNED BY public.sk_storage_cells.id;


--
-- Name: sk_vending_books; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.sk_vending_books (
    id integer NOT NULL,
    vending_station_id uuid NOT NULL,
    cell_number integer,
    book_code text NOT NULL,
    book_name text,
    master_id integer NOT NULL,
    user_code text,
    date_in timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    date_out timestamp with time zone
);


--
-- Name: sk_vending_books_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.sk_vending_books_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: sk_vending_books_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.sk_vending_books_id_seq OWNED BY public.sk_vending_books.id;


--
-- Name: upd_version_descriptions; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.upd_version_descriptions (
    version_id integer NOT NULL,
    description text NOT NULL
);


--
-- Name: upd_versions; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.upd_versions (
    id integer NOT NULL,
    version text NOT NULL,
    date_created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
    is_current boolean DEFAULT true NOT NULL
);


--
-- Name: upd_versions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.upd_versions_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;


--
-- Name: upd_versions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.upd_versions_id_seq OWNED BY public.upd_versions.id;


--
-- Name: acs_entrances id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_entrances ALTER COLUMN id SET DEFAULT nextval('public.acs_entrances_id_seq'::regclass);


--
-- Name: acs_events_history id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_events_history ALTER COLUMN id SET DEFAULT nextval('public.acs_eventshistory_id_seq'::regclass);


--
-- Name: acs_reader_antennas id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_reader_antennas ALTER COLUMN id SET DEFAULT nextval('public.acs_readerantennas_id_seq'::regclass);


--
-- Name: acs_readers id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_readers ALTER COLUMN id SET DEFAULT nextval('public.acs_readers_id_seq'::regclass);


--
-- Name: acs_zones id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_zones ALTER COLUMN id SET DEFAULT nextval('public.acs_zones_id_seq'::regclass);


--
-- Name: auth_api_keys id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_api_keys ALTER COLUMN id SET DEFAULT nextval('public.auth_api_keys_id_seq'::regclass);


--
-- Name: auth_login_log_warnings id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_log_warnings ALTER COLUMN id SET DEFAULT nextval('public.auth_login_log_warnings_id_seq'::regclass);


--
-- Name: auth_login_logs id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_logs ALTER COLUMN id SET DEFAULT nextval('public.auth_login_logs_id_seq'::regclass);


--
-- Name: auth_logins id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_logins ALTER COLUMN id SET DEFAULT nextval('public.auth_logins_id_seq'::regclass);


--
-- Name: auth_sessions id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_sessions ALTER COLUMN id SET DEFAULT nextval('public.auth_sessions_id_seq'::regclass);


--
-- Name: bk_backgrounds id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_backgrounds ALTER COLUMN id SET DEFAULT nextval('public.bk_backgrounds_id_seq1'::regclass);


--
-- Name: bk_book_case_sections id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_case_sections ALTER COLUMN id SET DEFAULT nextval('public.bk_book_case_sections_id_seq'::regclass);


--
-- Name: bk_book_cases id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_cases ALTER COLUMN id SET DEFAULT nextval('public.bk_book_cases_id_seq'::regclass);


--
-- Name: bk_book_shelves id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_shelves ALTER COLUMN id SET DEFAULT nextval('public.bk_book_shelves_id_seq'::regclass);


--
-- Name: bk_environment id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_environment ALTER COLUMN id SET DEFAULT nextval('public.bk_environment_id_seq'::regclass);


--
-- Name: bk_history id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_history ALTER COLUMN id SET DEFAULT nextval('public.bk_history_id_seq'::regclass);


--
-- Name: fd_device_cameras id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_device_cameras ALTER COLUMN id SET DEFAULT nextval('public.fd_device_cameras_id_seq'::regclass);


--
-- Name: fd_devices id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_devices ALTER COLUMN id SET DEFAULT nextval('public.fd_devices_id_seq'::regclass);


--
-- Name: fd_logs id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_logs ALTER COLUMN id SET DEFAULT nextval('public.fd_logs_id_seq'::regclass);


--
-- Name: fd_visitors_photo id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_photo ALTER COLUMN id SET DEFAULT nextval('public.fd_visitors_photo_id_seq'::regclass);


--
-- Name: gpt_languages id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_languages ALTER COLUMN id SET DEFAULT nextval('public.gpt_languages_id_seq'::regclass);


--
-- Name: gpt_voice_interactions id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_interactions ALTER COLUMN id SET DEFAULT nextval('public.gpt_voice_interactions_id_seq'::regclass);


--
-- Name: gpt_voice_response_templates id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_response_templates ALTER COLUMN id SET DEFAULT nextval('public.gpt_voice_response_templates_id_seq'::regclass);


--
-- Name: gpt_voice_types id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_types ALTER COLUMN id SET DEFAULT nextval('public.gpt_voice_types_id_seq'::regclass);


--
-- Name: kpi_service_history id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_service_history ALTER COLUMN id SET DEFAULT nextval('public.kpi_service_history_id_seq'::regclass);


--
-- Name: kpi_services id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_services ALTER COLUMN id SET DEFAULT nextval('public.kpi_services_id_seq'::regclass);


--
-- Name: kpi_services_types id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_services_types ALTER COLUMN id SET DEFAULT nextval('public.kpi_services_types_id_seq'::regclass);


--
-- Name: lib_book_user_ratings id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_book_user_ratings ALTER COLUMN id SET DEFAULT nextval('public.lib_book_user_ratings_id_seq'::regclass);


--
-- Name: lib_books id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_books ALTER COLUMN id SET DEFAULT nextval('public.lib_books_id_seq'::regclass);


--
-- Name: mon_book_shelves_stat id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_book_shelves_stat ALTER COLUMN id SET DEFAULT nextval('public.mon_book_shelves_stat_id_seq'::regclass);


--
-- Name: mon_device_data id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_device_data ALTER COLUMN id SET DEFAULT nextval('public.mon_device_data_id_seq'::regclass);


--
-- Name: mon_gates_counters id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_counters ALTER COLUMN id SET DEFAULT nextval('public.mon_gates_counters_id_seq'::regclass);


--
-- Name: mon_gates_eas id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_eas ALTER COLUMN id SET DEFAULT nextval('public.mon_gates_eas_id_seq'::regclass);


--
-- Name: mon_gates_event_history id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_event_history ALTER COLUMN id SET DEFAULT nextval('public.mon_gates_event_history_id_seq'::regclass);


--
-- Name: mon_services id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_services ALTER COLUMN id SET DEFAULT nextval('public.mon_services_id_seq'::regclass);


--
-- Name: mon_stations_banners id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_banners ALTER COLUMN id SET DEFAULT nextval('public.mon_stations_banners_id_seq'::regclass);


--
-- Name: mon_stations_event_history id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_event_history ALTER COLUMN id SET DEFAULT nextval('public.mon_stations_event_history_id_seq'::regclass);


--
-- Name: mon_stations_monitoring id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_monitoring ALTER COLUMN id SET DEFAULT nextval('public.mon_stations_monitoring_id_seq'::regclass);


--
-- Name: monitor_libraries_snapshot id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_libraries_snapshot ALTER COLUMN id SET DEFAULT nextval('public.monitor_libraries_snapshot_id_seq'::regclass);


--
-- Name: rdr_reader_photos id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.rdr_reader_photos ALTER COLUMN id SET DEFAULT nextval('public.rdr_reader_photos_id_seq'::regclass);


--
-- Name: rdr_readers id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.rdr_readers ALTER COLUMN id SET DEFAULT nextval('public.rdr_readers_id_seq'::regclass);


--
-- Name: sk_history id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history ALTER COLUMN id SET DEFAULT nextval('public.sk_history_id_seq'::regclass);


--
-- Name: sk_history_master id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history_master ALTER COLUMN id SET DEFAULT nextval('public.sk_history_master_id_seq'::regclass);


--
-- Name: sk_masters_rfid id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_masters_rfid ALTER COLUMN id SET DEFAULT nextval('public.sk_masters_rfid_id_seq'::regclass);


--
-- Name: sk_order_body id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_order_body ALTER COLUMN id SET DEFAULT nextval('public.sk_order_body_id_seq'::regclass);


--
-- Name: sk_orders id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_orders ALTER COLUMN id SET DEFAULT nextval('public.sk_orders_id_seq'::regclass);


--
-- Name: sk_returns id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_returns ALTER COLUMN id SET DEFAULT nextval('public.sk_returns_id_seq'::regclass);


--
-- Name: sk_returns_body id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_returns_body ALTER COLUMN id SET DEFAULT nextval('public.sk_returns_body_id_seq'::regclass);


--
-- Name: sk_storage_cells id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_storage_cells ALTER COLUMN id SET DEFAULT nextval('public.sk_storage_cells_id_seq'::regclass);


--
-- Name: sk_vending_books id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_vending_books ALTER COLUMN id SET DEFAULT nextval('public.sk_vending_books_id_seq'::regclass);


--
-- Name: upd_versions id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.upd_versions ALTER COLUMN id SET DEFAULT nextval('public.upd_versions_id_seq'::regclass);


--
-- Name: attach_files attach_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.attach_files
    ADD CONSTRAINT attach_files_pkey PRIMARY KEY (uid);


--
-- Name: auth_api_keys auth_api_keys_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_api_keys
    ADD CONSTRAINT auth_api_keys_pkey PRIMARY KEY (id);


--
-- Name: auth_login_log_warnings auth_login_log_warnings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_log_warnings
    ADD CONSTRAINT auth_login_log_warnings_pkey PRIMARY KEY (id);


--
-- Name: auth_login_logs auth_login_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_logs
    ADD CONSTRAINT auth_login_logs_pkey PRIMARY KEY (id);


--
-- Name: auth_login_roles_ext auth_login_roles_ext_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_roles_ext
    ADD CONSTRAINT auth_login_roles_ext_pkey PRIMARY KEY (login_id);


--
-- Name: bk_history bk_history_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_history
    ADD CONSTRAINT bk_history_pkey PRIMARY KEY (id);


--
-- Name: call_logs call_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.call_logs
    ADD CONSTRAINT call_logs_pkey PRIMARY KEY (id);


--
-- Name: log_common log_common_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_common
    ADD CONSTRAINT log_common_pkey PRIMARY KEY (id);


--
-- Name: log_easybookrfid log_easybookrfid_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_easybookrfid
    ADD CONSTRAINT log_easybookrfid_pkey PRIMARY KEY (id);


--
-- Name: log_gates log_gates_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_gates
    ADD CONSTRAINT log_gates_pkey PRIMARY KEY (id);


--
-- Name: log_login log_login_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_login
    ADD CONSTRAINT log_login_pkey PRIMARY KEY (id);


--
-- Name: log_patrons log_patrons_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_patrons
    ADD CONSTRAINT log_patrons_pkey PRIMARY KEY (id);


--
-- Name: log_safekeepers log_safekeepers_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_safekeepers
    ADD CONSTRAINT log_safekeepers_pkey PRIMARY KEY (id);


--
-- Name: log_smartshelves log_smartshelves_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_smartshelves
    ADD CONSTRAINT log_smartshelves_pkey PRIMARY KEY (id);


--
-- Name: log_stat log_stat_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_stat
    ADD CONSTRAINT log_stat_pkey PRIMARY KEY (id);


--
-- Name: log_stations log_stations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_stations
    ADD CONSTRAINT log_stations_pkey PRIMARY KEY (id);


--
-- Name: log_unknown log_unknown_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_unknown
    ADD CONSTRAINT log_unknown_pkey PRIMARY KEY (id);


--
-- Name: log_users log_users_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_users
    ADD CONSTRAINT log_users_pkey PRIMARY KEY (id);


--
-- Name: log_vendings log_vendings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.log_vendings
    ADD CONSTRAINT log_vendings_pkey PRIMARY KEY (id);


--
-- Name: acs_reader_antennas pk_acs_antennas; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_reader_antennas
    ADD CONSTRAINT pk_acs_antennas PRIMARY KEY (id);


--
-- Name: acs_direction_types pk_acs_directiontypes; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_direction_types
    ADD CONSTRAINT pk_acs_directiontypes PRIMARY KEY (id);


--
-- Name: acs_entrances pk_acs_entrances; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_entrances
    ADD CONSTRAINT pk_acs_entrances PRIMARY KEY (id);


--
-- Name: acs_events_history pk_acs_eventshistory; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_events_history
    ADD CONSTRAINT pk_acs_eventshistory PRIMARY KEY (id);


--
-- Name: acs_readers pk_acs_readers; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_readers
    ADD CONSTRAINT pk_acs_readers PRIMARY KEY (id);


--
-- Name: acs_readers_types pk_acs_readerstypes; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_readers_types
    ADD CONSTRAINT pk_acs_readerstypes PRIMARY KEY (id);


--
-- Name: acs_zones pk_acs_zones; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_zones
    ADD CONSTRAINT pk_acs_zones PRIMARY KEY (id);


--
-- Name: auth_logins pk_auth_login_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_logins
    ADD CONSTRAINT pk_auth_login_1 PRIMARY KEY (id);


--
-- Name: auth_login_roles pk_auth_login_roles; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_roles
    ADD CONSTRAINT pk_auth_login_roles PRIMARY KEY (id);


--
-- Name: auth_role_types pk_auth_roletype_2; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_role_types
    ADD CONSTRAINT pk_auth_roletype_2 PRIMARY KEY (id);


--
-- Name: auth_sessions pk_auth_sessions_id; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_sessions
    ADD CONSTRAINT pk_auth_sessions_id PRIMARY KEY (id);


--
-- Name: bk_books pk_bk_b; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_books
    ADD CONSTRAINT pk_bk_b PRIMARY KEY (book_id);


--
-- Name: bk_backgrounds pk_bk_backgrounds; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_backgrounds
    ADD CONSTRAINT pk_bk_backgrounds PRIMARY KEY (id);


--
-- Name: bk_book_cases pk_bk_book_cases; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_cases
    ADD CONSTRAINT pk_bk_book_cases PRIMARY KEY (id);


--
-- Name: bk_book_cells pk_bk_book_cells; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_cells
    ADD CONSTRAINT pk_bk_book_cells PRIMARY KEY (id);


--
-- Name: bk_book_inv_states pk_bk_book_inv_states; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_inv_states
    ADD CONSTRAINT pk_bk_book_inv_states PRIMARY KEY (id);


--
-- Name: bk_book_shelves pk_bk_book_shelves; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_shelves
    ADD CONSTRAINT pk_bk_book_shelves PRIMARY KEY (id);


--
-- Name: bk_book_states pk_bk_book_states; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_states
    ADD CONSTRAINT pk_bk_book_states PRIMARY KEY (id);


--
-- Name: bk_books_cache pk_bk_books_cache; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_books_cache
    ADD CONSTRAINT pk_bk_books_cache PRIMARY KEY (book_code, library_id);


--
-- Name: bk_environment pk_bk_environment; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_environment
    ADD CONSTRAINT pk_bk_environment PRIMARY KEY (id);


--
-- Name: bk_book_case_sections pk_bk_sections; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_case_sections
    ADD CONSTRAINT pk_bk_sections PRIMARY KEY (id);


--
-- Name: dir_days pk_dir_days; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.dir_days
    ADD CONSTRAINT pk_dir_days PRIMARY KEY (day_value);


--
-- Name: dir_hours_day pk_dir_hours_day; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.dir_hours_day
    ADD CONSTRAINT pk_dir_hours_day PRIMARY KEY (hour_value);


--
-- Name: fd_device_cameras pk_fd_device_cameras; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_device_cameras
    ADD CONSTRAINT pk_fd_device_cameras PRIMARY KEY (id);


--
-- Name: fd_devices pk_fd_devices; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_devices
    ADD CONSTRAINT pk_fd_devices PRIMARY KEY (id);


--
-- Name: fd_logs pk_fd_logs; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_logs
    ADD CONSTRAINT pk_fd_logs PRIMARY KEY (id);


--
-- Name: fd_visitors_full_cadre pk_fd_visitors_full_cadre; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_full_cadre
    ADD CONSTRAINT pk_fd_visitors_full_cadre PRIMARY KEY (visitor_photo_id);


--
-- Name: fd_visitors_photo pk_fd_visitors_photo; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_photo
    ADD CONSTRAINT pk_fd_visitors_photo PRIMARY KEY (id);


--
-- Name: fd_visitors_values pk_fd_visitors_values; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_values
    ADD CONSTRAINT pk_fd_visitors_values PRIMARY KEY (visitor_photo_id);


--
-- Name: internal pk_internal; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.internal
    ADD CONSTRAINT pk_internal PRIMARY KEY (id);


--
-- Name: kpi_service_history pk_kpi_service_history; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_service_history
    ADD CONSTRAINT pk_kpi_service_history PRIMARY KEY (id);


--
-- Name: kpi_services pk_kpi_services; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_services
    ADD CONSTRAINT pk_kpi_services PRIMARY KEY (id);


--
-- Name: kpi_services_types pk_kpi_services_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_services_types
    ADD CONSTRAINT pk_kpi_services_types PRIMARY KEY (id);


--
-- Name: gpt_languages pk_languages; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_languages
    ADD CONSTRAINT pk_languages PRIMARY KEY (id);


--
-- Name: lib_book_user_ratings pk_lib_book_user_ratings; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_book_user_ratings
    ADD CONSTRAINT pk_lib_book_user_ratings PRIMARY KEY (id);


--
-- Name: lib_books_covers pk_lib_books_covers; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_books_covers
    ADD CONSTRAINT pk_lib_books_covers PRIMARY KEY (book_id);


--
-- Name: lib_libraries pk_lib_libraries_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_libraries
    ADD CONSTRAINT pk_lib_libraries_1 PRIMARY KEY (id);


--
-- Name: main_modules pk_main_modules; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.main_modules
    ADD CONSTRAINT pk_main_modules PRIMARY KEY (id);


--
-- Name: mon_book_shelves_stat pk_mon_book_shelves_stat; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_book_shelves_stat
    ADD CONSTRAINT pk_mon_book_shelves_stat PRIMARY KEY (id);


--
-- Name: mon_device_data pk_mon_device_data; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_device_data
    ADD CONSTRAINT pk_mon_device_data PRIMARY KEY (id);


--
-- Name: mon_device_cfg_types pk_mon_device_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_device_cfg_types
    ADD CONSTRAINT pk_mon_device_types PRIMARY KEY (id);


--
-- Name: mon_device_types pk_mon_device_types_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_device_types
    ADD CONSTRAINT pk_mon_device_types_1 PRIMARY KEY (id);


--
-- Name: mon_devices pk_mon_devices; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_devices
    ADD CONSTRAINT pk_mon_devices PRIMARY KEY (id);


--
-- Name: lib_books pk_mon_gate_books; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_books
    ADD CONSTRAINT pk_mon_gate_books PRIMARY KEY (id);


--
-- Name: mon_gate_cfg_types pk_mon_gate_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gate_cfg_types
    ADD CONSTRAINT pk_mon_gate_types PRIMARY KEY (id);


--
-- Name: mon_gate_types pk_mon_gate_types_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gate_types
    ADD CONSTRAINT pk_mon_gate_types_1 PRIMARY KEY (id);


--
-- Name: mon_gates pk_mon_gates; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates
    ADD CONSTRAINT pk_mon_gates PRIMARY KEY (id);


--
-- Name: mon_gates_counters pk_mon_gates_counters; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_counters
    ADD CONSTRAINT pk_mon_gates_counters PRIMARY KEY (id);


--
-- Name: mon_gates_eas pk_mon_gates_eas; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_eas
    ADD CONSTRAINT pk_mon_gates_eas PRIMARY KEY (id);


--
-- Name: mon_gates_event_history pk_mon_gates_event_history; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_event_history
    ADD CONSTRAINT pk_mon_gates_event_history PRIMARY KEY (id);


--
-- Name: mon_mono_gate_count_devices pk_mon_mono_gate_count_devices; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_mono_gate_count_devices
    ADD CONSTRAINT pk_mon_mono_gate_count_devices PRIMARY KEY (id);


--
-- Name: sk_safekeeper_types pk_mon_safe_keeper_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_safekeeper_types
    ADD CONSTRAINT pk_mon_safe_keeper_types PRIMARY KEY (id);


--
-- Name: mon_service_types pk_mon_service_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_service_types
    ADD CONSTRAINT pk_mon_service_types PRIMARY KEY (id);


--
-- Name: mon_services pk_mon_services; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_services
    ADD CONSTRAINT pk_mon_services PRIMARY KEY (id);


--
-- Name: mon_stations_banners pk_mon_station_banners; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_banners
    ADD CONSTRAINT pk_mon_station_banners PRIMARY KEY (id);


--
-- Name: mon_station_types pk_mon_station_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_station_types
    ADD CONSTRAINT pk_mon_station_types PRIMARY KEY (id);


--
-- Name: mon_stations pk_mon_stations; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations
    ADD CONSTRAINT pk_mon_stations PRIMARY KEY (id);


--
-- Name: mon_stations_banners_tasks pk_mon_stations_banners_tasks; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_banners_tasks
    ADD CONSTRAINT pk_mon_stations_banners_tasks PRIMARY KEY (banner_id, station_id);


--
-- Name: mon_stations_config pk_mon_stations_config; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_config
    ADD CONSTRAINT pk_mon_stations_config PRIMARY KEY (station_id);


--
-- Name: mon_stations_event_history pk_mon_stations_event_history; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_event_history
    ADD CONSTRAINT pk_mon_stations_event_history PRIMARY KEY (id);


--
-- Name: mon_event_types pk_mon_stations_event_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_event_types
    ADD CONSTRAINT pk_mon_stations_event_types PRIMARY KEY (id);


--
-- Name: mon_events pk_mon_stations_events; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_events
    ADD CONSTRAINT pk_mon_stations_events PRIMARY KEY (id);


--
-- Name: mon_stations_monitoring pk_mon_stations_monitoring; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_monitoring
    ADD CONSTRAINT pk_mon_stations_monitoring PRIMARY KEY (id);


--
-- Name: monitor_device_data pk_monitor_device_data_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_device_data
    ADD CONSTRAINT pk_monitor_device_data_1 PRIMARY KEY (id_key);


--
-- Name: monitor_device_types pk_monitor_device_types_id; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_device_types
    ADD CONSTRAINT pk_monitor_device_types_id PRIMARY KEY (id);


--
-- Name: monitor_devices pk_monitor_devices_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_devices
    ADD CONSTRAINT pk_monitor_devices_1 PRIMARY KEY (device_id);


--
-- Name: monitor_gates pk_monitor_gates; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_gates
    ADD CONSTRAINT pk_monitor_gates PRIMARY KEY (id);


--
-- Name: monitor_gates_counters pk_monitor_gates_counters_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_gates_counters
    ADD CONSTRAINT pk_monitor_gates_counters_1 PRIMARY KEY (id_key);


--
-- Name: monitor_gates_eas pk_monitor_gates_eas_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_gates_eas
    ADD CONSTRAINT pk_monitor_gates_eas_1 PRIMARY KEY (id_key);


--
-- Name: monitor_gates_event_history pk_monitor_gates_event_history; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_gates_event_history
    ADD CONSTRAINT pk_monitor_gates_event_history PRIMARY KEY (id_key);


--
-- Name: monitor_internals pk_monitor_internals; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_internals
    ADD CONSTRAINT pk_monitor_internals PRIMARY KEY (guid);


--
-- Name: monitor_internals_snapshot pk_monitor_internals_snapshot; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_internals_snapshot
    ADD CONSTRAINT pk_monitor_internals_snapshot PRIMARY KEY (guid);


--
-- Name: monitor_libraries pk_monitor_libraries; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_libraries
    ADD CONSTRAINT pk_monitor_libraries PRIMARY KEY (id);


--
-- Name: monitor_libraries_snapshot pk_monitor_libraries_snapshot; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_libraries_snapshot
    ADD CONSTRAINT pk_monitor_libraries_snapshot PRIMARY KEY (id);


--
-- Name: monitor_owners pk_monitor_owners; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_owners
    ADD CONSTRAINT pk_monitor_owners PRIMARY KEY (id);


--
-- Name: monitor_services_snapshot pk_monitor_services_snapshot; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_services_snapshot
    ADD CONSTRAINT pk_monitor_services_snapshot PRIMARY KEY (id);


--
-- Name: monitor_stations pk_monitor_stations; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_stations
    ADD CONSTRAINT pk_monitor_stations PRIMARY KEY (id);


--
-- Name: monitor_stations_event_history pk_monitor_stations_event_history_1; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_stations_event_history
    ADD CONSTRAINT pk_monitor_stations_event_history_1 PRIMARY KEY (id_key);


--
-- Name: owners pk_owners; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.owners
    ADD CONSTRAINT pk_owners PRIMARY KEY (id);


--
-- Name: monitor_owners_snapshot pk_owners_snapshop; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_owners_snapshot
    ADD CONSTRAINT pk_owners_snapshop PRIMARY KEY (id);


--
-- Name: prop_ais pk_prop_ais; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_ais
    ADD CONSTRAINT pk_prop_ais PRIMARY KEY (login_id);


--
-- Name: prop_common pk_prop_common; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_common
    ADD CONSTRAINT pk_prop_common PRIMARY KEY (login_id);


--
-- Name: prop_email pk_prop_email; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_email
    ADD CONSTRAINT pk_prop_email PRIMARY KEY (login_id);


--
-- Name: prop_face_detect pk_prop_face_detect; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_face_detect
    ADD CONSTRAINT pk_prop_face_detect PRIMARY KEY (login_id);


--
-- Name: prop_proxy_server pk_prop_proxy_server; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_proxy_server
    ADD CONSTRAINT pk_prop_proxy_server PRIMARY KEY (login_id);


--
-- Name: rdr_reader_photos pk_rdr_reader_photos; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.rdr_reader_photos
    ADD CONSTRAINT pk_rdr_reader_photos PRIMARY KEY (id);


--
-- Name: rdr_readers pk_rdr_readers; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.rdr_readers
    ADD CONSTRAINT pk_rdr_readers PRIMARY KEY (id);


--
-- Name: sk_event_types pk_sk_event_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_event_types
    ADD CONSTRAINT pk_sk_event_types PRIMARY KEY (id);


--
-- Name: sk_history pk_sk_history; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history
    ADD CONSTRAINT pk_sk_history PRIMARY KEY (id);


--
-- Name: sk_history_master pk_sk_history_master; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history_master
    ADD CONSTRAINT pk_sk_history_master PRIMARY KEY (id);


--
-- Name: sk_masters_rfid pk_sk_masters_rfid; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_masters_rfid
    ADD CONSTRAINT pk_sk_masters_rfid PRIMARY KEY (id);


--
-- Name: sk_order_body pk_sk_order_body; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_order_body
    ADD CONSTRAINT pk_sk_order_body PRIMARY KEY (id);


--
-- Name: sk_order_states pk_sk_order_states; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_order_states
    ADD CONSTRAINT pk_sk_order_states PRIMARY KEY (id);


--
-- Name: sk_orders pk_sk_orders; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_orders
    ADD CONSTRAINT pk_sk_orders PRIMARY KEY (id);


--
-- Name: sk_returns_body pk_sk_return_boby; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_returns_body
    ADD CONSTRAINT pk_sk_return_boby PRIMARY KEY (id);


--
-- Name: sk_returns pk_sk_returns; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_returns
    ADD CONSTRAINT pk_sk_returns PRIMARY KEY (id);


--
-- Name: sk_safekeepers pk_sk_safe_keepers; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_safekeepers
    ADD CONSTRAINT pk_sk_safe_keepers PRIMARY KEY (id);


--
-- Name: sk_safekeepers_masters_rfid pk_sk_safe_keepers_masters_rfid; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_safekeepers_masters_rfid
    ADD CONSTRAINT pk_sk_safe_keepers_masters_rfid PRIMARY KEY (master_rfid_id, safe_keeper_id);


--
-- Name: sk_storage_cells pk_sk_storage_cells; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_storage_cells
    ADD CONSTRAINT pk_sk_storage_cells PRIMARY KEY (id);


--
-- Name: sk_vending_books pk_sk_vending_books; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_vending_books
    ADD CONSTRAINT pk_sk_vending_books PRIMARY KEY (id);


--
-- Name: upd_versions pk_upd_versions; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.upd_versions
    ADD CONSTRAINT pk_upd_versions PRIMARY KEY (id);


--
-- Name: gpt_voice_assistant_settings pk_voice_assistant_settings; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_assistant_settings
    ADD CONSTRAINT pk_voice_assistant_settings PRIMARY KEY (station_id);


--
-- Name: gpt_voice_interactions pk_voice_interactions; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_interactions
    ADD CONSTRAINT pk_voice_interactions PRIMARY KEY (id);


--
-- Name: gpt_voice_response_templates pk_voice_response_templates; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_response_templates
    ADD CONSTRAINT pk_voice_response_templates PRIMARY KEY (id);


--
-- Name: gpt_voice_template_stations pk_voice_template_stations; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_template_stations
    ADD CONSTRAINT pk_voice_template_stations PRIMARY KEY (template_id, station_id);


--
-- Name: gpt_voice_types pk_voice_types; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_types
    ADD CONSTRAINT pk_voice_types PRIMARY KEY (id);


--
-- Name: gpt_languages uq_languages_code; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_languages
    ADD CONSTRAINT uq_languages_code UNIQUE (code);


--
-- Name: gpt_languages uq_languages_name; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_languages
    ADD CONSTRAINT uq_languages_name UNIQUE (name);


--
-- Name: gpt_voice_types uq_voice_types_name; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_types
    ADD CONSTRAINT uq_voice_types_name UNIQUE (name);


--
-- Name: bk_books_cell_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX bk_books_cell_id ON public.bk_books USING btree (cell_id);


--
-- Name: bk_books_state_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX bk_books_state_id ON public.bk_books USING btree (state_id) INCLUDE (cell_id);


--
-- Name: idx_attach_files_date_created; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_attach_files_date_created ON public.attach_files USING btree (date_created);


--
-- Name: idx_attach_files_file_size; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_attach_files_file_size ON public.attach_files USING btree (file_size);


--
-- Name: idx_auth_api_keys_login_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_api_keys_login_id ON public.auth_api_keys USING btree (login_id);


--
-- Name: idx_auth_login_log_warnings_date_created; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_login_log_warnings_date_created ON public.auth_login_log_warnings USING btree (date_created);


--
-- Name: idx_auth_login_log_warnings_summary_hash; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_login_log_warnings_summary_hash ON public.auth_login_log_warnings USING btree (summary_hash);


--
-- Name: idx_auth_login_logs_date_created; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_login_logs_date_created ON public.auth_login_logs USING btree (date_created);


--
-- Name: idx_auth_login_logs_ip; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_login_logs_ip ON public.auth_login_logs USING btree (ip);


--
-- Name: idx_auth_login_logs_is_success; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_login_logs_is_success ON public.auth_login_logs USING btree (is_success);


--
-- Name: idx_auth_login_logs_login_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_login_logs_login_id ON public.auth_login_logs USING btree (login_id);


--
-- Name: idx_auth_login_logs_summary_hash; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_auth_login_logs_summary_hash ON public.auth_login_logs USING btree (summary_hash);


--
-- Name: idx_call_logs_method_name; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_call_logs_method_name ON public.call_logs USING btree (method_name);


--
-- Name: idx_call_logs_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_call_logs_start_time ON public.call_logs USING btree (start_time DESC);


--
-- Name: idx_event_hist_date; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_event_hist_date ON public.mon_stations_event_history USING btree (date_event);


--
-- Name: idx_event_hist_station_event; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_event_hist_station_event ON public.mon_stations_event_history USING btree (station_id, event_id);


--
-- Name: idx_event_hist_user; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_event_hist_user ON public.mon_stations_event_history USING btree (user_code);


--
-- Name: idx_lib_libraries_owner_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_lib_libraries_owner_id ON public.lib_libraries USING btree (owner_id);


--
-- Name: idx_log_common_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_common_controller ON public.log_common USING btree (controller_name);


--
-- Name: idx_log_common_duration; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_common_duration ON public.log_common USING btree (duration_ms) WHERE (duration_ms IS NOT NULL);


--
-- Name: idx_log_common_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_common_start_time ON public.log_common USING btree (start_time DESC);


--
-- Name: idx_log_easybookrfid_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_easybookrfid_controller ON public.log_easybookrfid USING btree (controller_name);


--
-- Name: idx_log_easybookrfid_slow; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_easybookrfid_slow ON public.log_easybookrfid USING btree (duration_ms) WHERE (duration_ms > 1000);


--
-- Name: idx_log_easybookrfid_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_easybookrfid_start_time ON public.log_easybookrfid USING btree (start_time DESC);


--
-- Name: idx_log_gates_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_gates_controller ON public.log_gates USING btree (controller_name);


--
-- Name: idx_log_gates_duration; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_gates_duration ON public.log_gates USING btree (duration_ms) WHERE (duration_ms IS NOT NULL);


--
-- Name: idx_log_gates_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_gates_start_time ON public.log_gates USING btree (start_time DESC);


--
-- Name: idx_log_login_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_login_controller ON public.log_login USING btree (controller_name);


--
-- Name: idx_log_login_duration; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_login_duration ON public.log_login USING btree (duration_ms) WHERE (duration_ms IS NOT NULL);


--
-- Name: idx_log_login_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_login_start_time ON public.log_login USING btree (start_time DESC);


--
-- Name: idx_log_patrons_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_patrons_controller ON public.log_patrons USING btree (controller_name);


--
-- Name: idx_log_patrons_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_patrons_start_time ON public.log_patrons USING btree (start_time DESC);


--
-- Name: idx_log_safekeepers_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_safekeepers_controller ON public.log_safekeepers USING btree (controller_name);


--
-- Name: idx_log_safekeepers_duration; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_safekeepers_duration ON public.log_safekeepers USING btree (duration_ms) WHERE (duration_ms IS NOT NULL);


--
-- Name: idx_log_safekeepers_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_safekeepers_start_time ON public.log_safekeepers USING btree (start_time DESC);


--
-- Name: idx_log_smartshelves_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_smartshelves_controller ON public.log_smartshelves USING btree (controller_name);


--
-- Name: idx_log_smartshelves_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_smartshelves_start_time ON public.log_smartshelves USING btree (start_time DESC);


--
-- Name: idx_log_stat_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_stat_controller ON public.log_stat USING btree (controller_name);


--
-- Name: idx_log_stat_slow; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_stat_slow ON public.log_stat USING btree (duration_ms) WHERE (duration_ms > 1000);


--
-- Name: idx_log_stat_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_stat_start_time ON public.log_stat USING btree (start_time DESC);


--
-- Name: idx_log_stations_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_stations_controller ON public.log_stations USING btree (controller_name);


--
-- Name: idx_log_stations_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_stations_start_time ON public.log_stations USING btree (start_time DESC);


--
-- Name: idx_log_unknown_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_unknown_controller ON public.log_unknown USING btree (controller_name);


--
-- Name: idx_log_unknown_duration; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_unknown_duration ON public.log_unknown USING btree (duration_ms) WHERE (duration_ms IS NOT NULL);


--
-- Name: idx_log_unknown_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_unknown_start_time ON public.log_unknown USING btree (start_time DESC);


--
-- Name: idx_log_users_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_users_controller ON public.log_users USING btree (controller_name);


--
-- Name: idx_log_users_duration; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_users_duration ON public.log_users USING btree (duration_ms) WHERE (duration_ms IS NOT NULL);


--
-- Name: idx_log_users_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_users_start_time ON public.log_users USING btree (start_time DESC);


--
-- Name: idx_log_vendings_controller; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_vendings_controller ON public.log_vendings USING btree (controller_name);


--
-- Name: idx_log_vendings_start_time; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_log_vendings_start_time ON public.log_vendings USING btree (start_time DESC);


--
-- Name: idx_mon_gates_counters_uniq; Type: INDEX; Schema: public; Owner: -
--

CREATE UNIQUE INDEX idx_mon_gates_counters_uniq ON public.mon_gates_counters USING btree (gate_id, date_value);


--
-- Name: idx_mon_services_library_id_type_id; Type: INDEX; Schema: public; Owner: -
--

CREATE UNIQUE INDEX idx_mon_services_library_id_type_id ON public.mon_services USING btree (library_id, type_id);


--
-- Name: idx_mon_stations_library_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_mon_stations_library_id ON public.mon_stations USING btree (library_id) WHERE (station_type_id <> ALL (ARRAY[3, 4, 9]));


--
-- Name: idx_mon_stations_sn; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_mon_stations_sn ON public.mon_stations USING btree (sn);


--
-- Name: idx_mseh_date_event; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_mseh_date_event ON public.mon_stations_event_history USING btree (date_event);


--
-- Name: idx_mseh_date_event_station_event; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_mseh_date_event_station_event ON public.mon_stations_event_history USING btree (date_event, station_id, event_id) WHERE (event_id = ANY (ARRAY[8, 9, 10, 11, 13, 14, 23, 25, 26, 49]));


--
-- Name: idx_rdr_readers_abis_code; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_rdr_readers_abis_code ON public.rdr_readers USING btree (abis_code);


--
-- Name: idx_rdr_readers_rfid_code; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX idx_rdr_readers_rfid_code ON public.rdr_readers USING btree (rfid_code);


--
-- Name: ind_eas_monitoring_eas_system_id_date_created; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ind_eas_monitoring_eas_system_id_date_created ON public.mon_stations_monitoring USING btree (station_id, date_created);


--
-- Name: ind_lib_books_library_id_is_book; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ind_lib_books_library_id_is_book ON public.lib_books USING btree (is_book, library_id) INCLUDE (book_code, book_name, id);


--
-- Name: ind_mon_gates_eas2; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ind_mon_gates_eas2 ON public.mon_gates_eas USING btree (gate_id, date_value);


--
-- Name: ind_mon_stations_event_history_1; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ind_mon_stations_event_history_1 ON public.mon_stations_event_history USING btree (station_id, id) INCLUDE (count, date_event, event_id, message, pair_id, user_code);


--
-- Name: ind_sk_storage_cells_date_close; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ind_sk_storage_cells_date_close ON public.sk_storage_cells USING btree (date_close);


--
-- Name: ix_acs_eventshistory; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_acs_eventshistory ON public.acs_events_history USING btree (reader_id, event_date);


--
-- Name: ix_acs_readerantennas; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_acs_readerantennas ON public.acs_reader_antennas USING btree (reader_id, reader_port_number);


--
-- Name: ix_gpt_voice_response_templates_library_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_gpt_voice_response_templates_library_id ON public.gpt_voice_response_templates USING btree (library_id) INCLUDE (answer_text, border_color, font_color, font_size, id, is_active, question_text);


--
-- Name: ix_gpt_voice_template_stations_station_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_gpt_voice_template_stations_station_id ON public.gpt_voice_template_stations USING btree (station_id) INCLUDE (is_active, template_id);


--
-- Name: ix_lib_books_book_code_library_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_lib_books_book_code_library_id ON public.lib_books USING btree (library_id, book_code);


--
-- Name: ix_lib_books_book_name_library_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_lib_books_book_name_library_id ON public.lib_books USING btree (book_name, library_id);


--
-- Name: ix_mon_book_shelves_stat; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_book_shelves_stat ON public.mon_book_shelves_stat USING btree (book_id, station_id);


--
-- Name: ix_mon_device_data; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_device_data ON public.mon_device_data USING btree (device_id, date_value, hour_value);


--
-- Name: ix_mon_gates_counters; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_gates_counters ON public.mon_gates_counters USING btree (gate_id, date_value);


--
-- Name: ix_mon_gates_eas; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_gates_eas ON public.mon_gates_eas USING btree (gate_id, uid, date_value);


--
-- Name: ix_mon_gates_event_history; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_gates_event_history ON public.mon_gates_event_history USING btree (gate_id, event_id, date_value);


--
-- Name: ix_mon_stations; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_stations ON public.mon_stations USING btree (sn);


--
-- Name: ix_mon_stations_event_history; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_stations_event_history ON public.mon_stations_event_history USING btree (station_id, event_id, date_event) INCLUDE (pair_id);


--
-- Name: ix_mon_stations_event_history_event_id_message; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX ix_mon_stations_event_history_event_id_message ON public.mon_stations_event_history USING btree (event_id, message);


--
-- Name: auth_login_roles auth_login_roles_auth_role_types_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_roles
    ADD CONSTRAINT auth_login_roles_auth_role_types_fk FOREIGN KEY (role_type_id) REFERENCES public.auth_role_types(id);


--
-- Name: auth_sessions auth_sessions_auth_logins_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_sessions
    ADD CONSTRAINT auth_sessions_auth_logins_fk FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: acs_reader_antennas fk_acs_antennas_acs_readers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_reader_antennas
    ADD CONSTRAINT fk_acs_antennas_acs_readers FOREIGN KEY (reader_id) REFERENCES public.acs_readers(id);


--
-- Name: acs_entrances fk_acs_entrances_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_entrances
    ADD CONSTRAINT fk_acs_entrances_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: acs_events_history fk_acs_eventshistory_acs_readers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_events_history
    ADD CONSTRAINT fk_acs_eventshistory_acs_readers FOREIGN KEY (reader_id) REFERENCES public.acs_readers(id);


--
-- Name: acs_reader_antennas fk_acs_readerantennas_acs_zones; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_reader_antennas
    ADD CONSTRAINT fk_acs_readerantennas_acs_zones FOREIGN KEY (zone_id) REFERENCES public.acs_zones(id);


--
-- Name: acs_readers fk_acs_readers_acs_entrances; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_readers
    ADD CONSTRAINT fk_acs_readers_acs_entrances FOREIGN KEY (entrance_id) REFERENCES public.acs_entrances(id);


--
-- Name: acs_readers fk_acs_readers_acs_readerstypes; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_readers
    ADD CONSTRAINT fk_acs_readers_acs_readerstypes FOREIGN KEY (reader_type_id) REFERENCES public.acs_readers_types(id);


--
-- Name: acs_zones fk_acs_zones_acs_directiontypes; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_zones
    ADD CONSTRAINT fk_acs_zones_acs_directiontypes FOREIGN KEY (direction_type_id) REFERENCES public.acs_direction_types(id);


--
-- Name: acs_zones fk_acs_zones_acs_entrances; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.acs_zones
    ADD CONSTRAINT fk_acs_zones_acs_entrances FOREIGN KEY (entrance_id) REFERENCES public.acs_entrances(id);


--
-- Name: auth_api_keys fk_auth_api_keys_login_id; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_api_keys
    ADD CONSTRAINT fk_auth_api_keys_login_id FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: auth_login_roles fk_auth_login_roles_auth_logins; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_roles
    ADD CONSTRAINT fk_auth_login_roles_auth_logins FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: auth_login_roles fk_auth_login_roles_auth_role_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_roles
    ADD CONSTRAINT fk_auth_login_roles_auth_role_types FOREIGN KEY (role_type_id) REFERENCES public.auth_role_types(id);


--
-- Name: auth_login_roles_ext fk_auth_login_roles_ext_auth_logins; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.auth_login_roles_ext
    ADD CONSTRAINT fk_auth_login_roles_ext_auth_logins FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: bk_backgrounds fk_bk_backgrounds_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_backgrounds
    ADD CONSTRAINT fk_bk_backgrounds_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: bk_book_cases fk_bk_book_cases_bk_book_case_sections; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_cases
    ADD CONSTRAINT fk_bk_book_cases_bk_book_case_sections FOREIGN KEY (book_case_section_id) REFERENCES public.bk_book_case_sections(id);


--
-- Name: bk_book_cases fk_bk_book_cases_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_cases
    ADD CONSTRAINT fk_bk_book_cases_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: bk_book_cells fk_bk_book_cells_bk_book_shelves; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_cells
    ADD CONSTRAINT fk_bk_book_cells_bk_book_shelves FOREIGN KEY (book_shelf_id) REFERENCES public.bk_book_shelves(id);


--
-- Name: bk_book_shelves fk_bk_book_shelves_bk_book_cases; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_shelves
    ADD CONSTRAINT fk_bk_book_shelves_bk_book_cases FOREIGN KEY (book_case_id) REFERENCES public.bk_book_cases(id);


--
-- Name: bk_books fk_bk_books_bk_book_cells; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_books
    ADD CONSTRAINT fk_bk_books_bk_book_cells FOREIGN KEY (cell_id) REFERENCES public.bk_book_cells(id);


--
-- Name: bk_books fk_bk_books_bk_book_states; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_books
    ADD CONSTRAINT fk_bk_books_bk_book_states FOREIGN KEY (state_id) REFERENCES public.bk_book_states(id);


--
-- Name: bk_books_cache fk_bk_books_cache_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_books_cache
    ADD CONSTRAINT fk_bk_books_cache_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: bk_books fk_bk_books_lib_books; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_books
    ADD CONSTRAINT fk_bk_books_lib_books FOREIGN KEY (book_id) REFERENCES public.lib_books(id);


--
-- Name: bk_environment fk_bk_environment_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_environment
    ADD CONSTRAINT fk_bk_environment_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: bk_history fk_bk_history_book; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_history
    ADD CONSTRAINT fk_bk_history_book FOREIGN KEY (book_id) REFERENCES public.lib_books(id);


--
-- Name: bk_history fk_bk_history_book_cell; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_history
    ADD CONSTRAINT fk_bk_history_book_cell FOREIGN KEY (book_cell_id) REFERENCES public.bk_book_cells(id);


--
-- Name: bk_history fk_bk_history_executor; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_history
    ADD CONSTRAINT fk_bk_history_executor FOREIGN KEY (executor_id) REFERENCES public.auth_logins(id);


--
-- Name: bk_history fk_bk_history_inv_state; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_history
    ADD CONSTRAINT fk_bk_history_inv_state FOREIGN KEY (inv_state_id) REFERENCES public.bk_book_inv_states(id);


--
-- Name: bk_book_case_sections fk_bk_sections_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.bk_book_case_sections
    ADD CONSTRAINT fk_bk_sections_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: fd_device_cameras fk_fd_device_cameras_fd_devices; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_device_cameras
    ADD CONSTRAINT fk_fd_device_cameras_fd_devices FOREIGN KEY (device_id) REFERENCES public.fd_devices(id);


--
-- Name: fd_devices fk_fd_devices_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_devices
    ADD CONSTRAINT fk_fd_devices_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: fd_logs fk_fd_logs_fd_device_cameras; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_logs
    ADD CONSTRAINT fk_fd_logs_fd_device_cameras FOREIGN KEY (device_camera_id) REFERENCES public.fd_device_cameras(id);


--
-- Name: fd_logs fk_fd_logs_fd_devices; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_logs
    ADD CONSTRAINT fk_fd_logs_fd_devices FOREIGN KEY (device_id) REFERENCES public.fd_devices(id);


--
-- Name: fd_visitors_full_cadre fk_fd_visitors_full_cadre_fd_visitors_photo; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_full_cadre
    ADD CONSTRAINT fk_fd_visitors_full_cadre_fd_visitors_photo FOREIGN KEY (visitor_photo_id) REFERENCES public.fd_visitors_photo(id);


--
-- Name: fd_visitors_photo fk_fd_visitors_photo_fd_device_cameras; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_photo
    ADD CONSTRAINT fk_fd_visitors_photo_fd_device_cameras FOREIGN KEY (camera_id) REFERENCES public.fd_device_cameras(id);


--
-- Name: fd_visitors_photo fk_fd_visitors_photo_rdr_readers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_photo
    ADD CONSTRAINT fk_fd_visitors_photo_rdr_readers FOREIGN KEY (reader_id) REFERENCES public.rdr_readers(id);


--
-- Name: fd_visitors_values fk_fd_visitors_values_fd_visitors_photo; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.fd_visitors_values
    ADD CONSTRAINT fk_fd_visitors_values_fd_visitors_photo FOREIGN KEY (visitor_photo_id) REFERENCES public.fd_visitors_photo(id);


--
-- Name: gpt_voice_assistant_settings fk_gpt_voice_assistant_settings_gpt_languages; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_assistant_settings
    ADD CONSTRAINT fk_gpt_voice_assistant_settings_gpt_languages FOREIGN KEY (language_id) REFERENCES public.gpt_languages(id);


--
-- Name: gpt_voice_assistant_settings fk_gpt_voice_assistant_settings_gpt_voice_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_assistant_settings
    ADD CONSTRAINT fk_gpt_voice_assistant_settings_gpt_voice_types FOREIGN KEY (voice_type_id) REFERENCES public.gpt_voice_types(id);


--
-- Name: gpt_voice_assistant_settings fk_gpt_voice_assistant_settings_mon_stations; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_assistant_settings
    ADD CONSTRAINT fk_gpt_voice_assistant_settings_mon_stations FOREIGN KEY (station_id) REFERENCES public.mon_stations(id);


--
-- Name: kpi_service_history fk_kpi_service_history_auth_login; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_service_history
    ADD CONSTRAINT fk_kpi_service_history_auth_login FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: kpi_service_history fk_kpi_service_history_kpi_services; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_service_history
    ADD CONSTRAINT fk_kpi_service_history_kpi_services FOREIGN KEY (service_id) REFERENCES public.kpi_services(id);


--
-- Name: kpi_service_history fk_kpi_service_history_rdr_readers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_service_history
    ADD CONSTRAINT fk_kpi_service_history_rdr_readers FOREIGN KEY (reader_id) REFERENCES public.rdr_readers(id);


--
-- Name: kpi_services fk_kpi_services_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_services
    ADD CONSTRAINT fk_kpi_services_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: lib_book_user_ratings fk_lib_book_user_ratings_lib_books; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_book_user_ratings
    ADD CONSTRAINT fk_lib_book_user_ratings_lib_books FOREIGN KEY (book_id) REFERENCES public.lib_books(id);


--
-- Name: lib_book_user_ratings fk_lib_book_user_ratings_rdr_readers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_book_user_ratings
    ADD CONSTRAINT fk_lib_book_user_ratings_rdr_readers FOREIGN KEY (reader_id) REFERENCES public.rdr_readers(id);


--
-- Name: lib_books fk_lib_books_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_books
    ADD CONSTRAINT fk_lib_books_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: lib_libraries fk_lib_libraries_owners; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_libraries
    ADD CONSTRAINT fk_lib_libraries_owners FOREIGN KEY (owner_id) REFERENCES public.owners(id);


--
-- Name: mon_book_shelves_stat fk_mon_book_shelves_stat_lib_books; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_book_shelves_stat
    ADD CONSTRAINT fk_mon_book_shelves_stat_lib_books FOREIGN KEY (book_id) REFERENCES public.lib_books(id);


--
-- Name: mon_book_shelves_stat fk_mon_book_shelves_stat_mon_stations; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_book_shelves_stat
    ADD CONSTRAINT fk_mon_book_shelves_stat_mon_stations FOREIGN KEY (station_id) REFERENCES public.mon_stations(id);


--
-- Name: mon_device_data fk_mon_device_data_mon_devices; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_device_data
    ADD CONSTRAINT fk_mon_device_data_mon_devices FOREIGN KEY (device_id) REFERENCES public.mon_devices(id);


--
-- Name: mon_devices fk_mon_devices_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_devices
    ADD CONSTRAINT fk_mon_devices_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: mon_devices fk_mon_devices_mon_device_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_devices
    ADD CONSTRAINT fk_mon_devices_mon_device_types FOREIGN KEY (type_id) REFERENCES public.mon_device_types(id);


--
-- Name: mon_events fk_mon_events_mon_event_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_events
    ADD CONSTRAINT fk_mon_events_mon_event_types FOREIGN KEY (event_type_id) REFERENCES public.mon_event_types(id);


--
-- Name: mon_gate_cfg_types fk_mon_gate_cfg_types_mon_gate_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gate_cfg_types
    ADD CONSTRAINT fk_mon_gate_cfg_types_mon_gate_types FOREIGN KEY (type_id) REFERENCES public.mon_gate_types(id);


--
-- Name: mon_gates_counters fk_mon_gates_counters_mon_gates; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_counters
    ADD CONSTRAINT fk_mon_gates_counters_mon_gates FOREIGN KEY (gate_id) REFERENCES public.mon_gates(id);


--
-- Name: mon_gates_eas fk_mon_gates_eas_mon_gates; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_eas
    ADD CONSTRAINT fk_mon_gates_eas_mon_gates FOREIGN KEY (gate_id) REFERENCES public.mon_gates(id);


--
-- Name: mon_gates_event_history fk_mon_gates_event_history_mon_gates; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates_event_history
    ADD CONSTRAINT fk_mon_gates_event_history_mon_gates FOREIGN KEY (gate_id) REFERENCES public.mon_gates(id);


--
-- Name: mon_gates fk_mon_gates_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates
    ADD CONSTRAINT fk_mon_gates_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: mon_gates fk_mon_gates_mon_gate_cfg_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates
    ADD CONSTRAINT fk_mon_gates_mon_gate_cfg_types FOREIGN KEY (cfg_type_id) REFERENCES public.mon_gate_cfg_types(id);


--
-- Name: mon_gates fk_mon_gates_mon_gate_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_gates
    ADD CONSTRAINT fk_mon_gates_mon_gate_types FOREIGN KEY (type_id) REFERENCES public.mon_gate_types(id);


--
-- Name: mon_mono_gate_count_devices fk_mon_mono_gate_count_devices_mon_gates; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_mono_gate_count_devices
    ADD CONSTRAINT fk_mon_mono_gate_count_devices_mon_gates FOREIGN KEY (gate_id) REFERENCES public.mon_gates(id);


--
-- Name: mon_services fk_mon_services_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_services
    ADD CONSTRAINT fk_mon_services_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: mon_services fk_mon_services_mon_service_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_services
    ADD CONSTRAINT fk_mon_services_mon_service_types FOREIGN KEY (type_id) REFERENCES public.mon_service_types(id);


--
-- Name: mon_stations_banners fk_mon_stations_banners_auth_login; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_banners
    ADD CONSTRAINT fk_mon_stations_banners_auth_login FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: mon_stations_banners_tasks fk_mon_stations_banners_tasks_mon_stations; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_banners_tasks
    ADD CONSTRAINT fk_mon_stations_banners_tasks_mon_stations FOREIGN KEY (station_id) REFERENCES public.mon_stations(id);


--
-- Name: mon_stations_banners_tasks fk_mon_stations_banners_tasks_mon_stations_banners; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_banners_tasks
    ADD CONSTRAINT fk_mon_stations_banners_tasks_mon_stations_banners FOREIGN KEY (banner_id) REFERENCES public.mon_stations_banners(id);


--
-- Name: mon_stations_banners_tasks fk_mon_stations_banners_tasks_owners; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_banners_tasks
    ADD CONSTRAINT fk_mon_stations_banners_tasks_owners FOREIGN KEY (owner_id) REFERENCES public.owners(id);


--
-- Name: mon_stations_event_history fk_mon_stations_event_history_mon_events; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_event_history
    ADD CONSTRAINT fk_mon_stations_event_history_mon_events FOREIGN KEY (event_id) REFERENCES public.mon_events(id);


--
-- Name: mon_stations_event_history fk_mon_stations_event_history_mon_stations; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_event_history
    ADD CONSTRAINT fk_mon_stations_event_history_mon_stations FOREIGN KEY (station_id) REFERENCES public.mon_stations(id);


--
-- Name: mon_stations fk_mon_stations_lib_libraries; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations
    ADD CONSTRAINT fk_mon_stations_lib_libraries FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: mon_stations fk_mon_stations_mon_station_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations
    ADD CONSTRAINT fk_mon_stations_mon_station_types FOREIGN KEY (station_type_id) REFERENCES public.mon_station_types(id);


--
-- Name: mon_stations_monitoring fk_mon_stations_monitoring_mon_stations; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.mon_stations_monitoring
    ADD CONSTRAINT fk_mon_stations_monitoring_mon_stations FOREIGN KEY (station_id) REFERENCES public.mon_stations(id);


--
-- Name: monitor_libraries fk_monitor_libraries_monitor_owners; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.monitor_libraries
    ADD CONSTRAINT fk_monitor_libraries_monitor_owners FOREIGN KEY (owner_id) REFERENCES public.monitor_owners(id);


--
-- Name: owners fk_owners_internal; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.owners
    ADD CONSTRAINT fk_owners_internal FOREIGN KEY (internal_id) REFERENCES public.internal(id);


--
-- Name: prop_common fk_prop_common_auth_logins; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_common
    ADD CONSTRAINT fk_prop_common_auth_logins FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: rdr_reader_photos fk_rdr_reader_photos_rdr_readers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.rdr_reader_photos
    ADD CONSTRAINT fk_rdr_reader_photos_rdr_readers FOREIGN KEY (reader_id) REFERENCES public.rdr_readers(id);


--
-- Name: rdr_readers fk_rdr_readers_owners; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.rdr_readers
    ADD CONSTRAINT fk_rdr_readers_owners FOREIGN KEY (owner_id) REFERENCES public.owners(id);


--
-- Name: sk_history_master fk_sk_history_master_sk_event_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history_master
    ADD CONSTRAINT fk_sk_history_master_sk_event_types FOREIGN KEY (event_type_id) REFERENCES public.sk_event_types(id);


--
-- Name: sk_history_master fk_sk_history_master_sk_masters_rfid; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history_master
    ADD CONSTRAINT fk_sk_history_master_sk_masters_rfid FOREIGN KEY (master_rfid_id) REFERENCES public.sk_masters_rfid(id);


--
-- Name: sk_history_master fk_sk_history_master_sk_safe_keepers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history_master
    ADD CONSTRAINT fk_sk_history_master_sk_safe_keepers FOREIGN KEY (safe_keeper_id) REFERENCES public.sk_safekeepers(id);


--
-- Name: sk_history fk_sk_history_sk_event_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history
    ADD CONSTRAINT fk_sk_history_sk_event_types FOREIGN KEY (event_type_id) REFERENCES public.sk_event_types(id);


--
-- Name: sk_history fk_sk_history_sk_safe_keepers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_history
    ADD CONSTRAINT fk_sk_history_sk_safe_keepers FOREIGN KEY (safe_keeper_id) REFERENCES public.sk_safekeepers(id);


--
-- Name: sk_masters_rfid fk_sk_masters_rfid_owners; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_masters_rfid
    ADD CONSTRAINT fk_sk_masters_rfid_owners FOREIGN KEY (owner_id) REFERENCES public.owners(id);


--
-- Name: sk_order_body fk_sk_order_body_sk_orders; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_order_body
    ADD CONSTRAINT fk_sk_order_body_sk_orders FOREIGN KEY (order_id) REFERENCES public.sk_orders(id);


--
-- Name: sk_orders fk_sk_orders_auth_login; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_orders
    ADD CONSTRAINT fk_sk_orders_auth_login FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: sk_orders fk_sk_orders_sk_order_states; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_orders
    ADD CONSTRAINT fk_sk_orders_sk_order_states FOREIGN KEY (state_id) REFERENCES public.sk_order_states(id);


--
-- Name: sk_orders fk_sk_orders_sk_safe_keepers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_orders
    ADD CONSTRAINT fk_sk_orders_sk_safe_keepers FOREIGN KEY (safe_keeper_id) REFERENCES public.sk_safekeepers(id);


--
-- Name: sk_safekeepers_masters_rfid fk_sk_safe_keepers_masters_rfid_sk_masters_rfid; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_safekeepers_masters_rfid
    ADD CONSTRAINT fk_sk_safe_keepers_masters_rfid_sk_masters_rfid FOREIGN KEY (master_rfid_id) REFERENCES public.sk_masters_rfid(id);


--
-- Name: sk_safekeepers_masters_rfid fk_sk_safe_keepers_masters_rfid_sk_safe_keepers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_safekeepers_masters_rfid
    ADD CONSTRAINT fk_sk_safe_keepers_masters_rfid_sk_safe_keepers FOREIGN KEY (safe_keeper_id) REFERENCES public.sk_safekeepers(id);


--
-- Name: sk_safekeepers fk_sk_safe_keepers_sk_safe_keeper_types; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_safekeepers
    ADD CONSTRAINT fk_sk_safe_keepers_sk_safe_keeper_types FOREIGN KEY (safe_keeper_type_id) REFERENCES public.sk_safekeeper_types(id);


--
-- Name: sk_storage_cells fk_sk_storage_cells_sk_safe_keepers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_storage_cells
    ADD CONSTRAINT fk_sk_storage_cells_sk_safe_keepers FOREIGN KEY (safe_keeper_id) REFERENCES public.sk_safekeepers(id);


--
-- Name: sk_vending_books fk_sk_vending_books_sk_masters_rfid; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_vending_books
    ADD CONSTRAINT fk_sk_vending_books_sk_masters_rfid FOREIGN KEY (master_id) REFERENCES public.sk_masters_rfid(id);


--
-- Name: sk_vending_books fk_sk_vending_books_sk_safe_keepers; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_vending_books
    ADD CONSTRAINT fk_sk_vending_books_sk_safe_keepers FOREIGN KEY (vending_station_id) REFERENCES public.sk_safekeepers(id);


--
-- Name: upd_version_descriptions fk_upd_version_descriptions_upd_versions; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.upd_version_descriptions
    ADD CONSTRAINT fk_upd_version_descriptions_upd_versions FOREIGN KEY (version_id) REFERENCES public.upd_versions(id);


--
-- Name: gpt_voice_interactions fk_voice_interactions_library; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_interactions
    ADD CONSTRAINT fk_voice_interactions_library FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: gpt_voice_interactions fk_voice_interactions_station; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_interactions
    ADD CONSTRAINT fk_voice_interactions_station FOREIGN KEY (station_id) REFERENCES public.mon_stations(id);


--
-- Name: gpt_voice_interactions fk_voice_interactions_template; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_interactions
    ADD CONSTRAINT fk_voice_interactions_template FOREIGN KEY (template_id) REFERENCES public.gpt_voice_response_templates(id);


--
-- Name: gpt_voice_response_templates fk_voice_response_templates_library; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_response_templates
    ADD CONSTRAINT fk_voice_response_templates_library FOREIGN KEY (library_id) REFERENCES public.lib_libraries(id);


--
-- Name: gpt_voice_template_stations fk_voice_template_stations_station; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_template_stations
    ADD CONSTRAINT fk_voice_template_stations_station FOREIGN KEY (station_id) REFERENCES public.mon_stations(id);


--
-- Name: gpt_voice_template_stations fk_voice_template_stations_template; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.gpt_voice_template_stations
    ADD CONSTRAINT fk_voice_template_stations_template FOREIGN KEY (template_id) REFERENCES public.gpt_voice_response_templates(id) ON DELETE CASCADE;


--
-- Name: kpi_services kpi_services_kpi_services_types_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.kpi_services
    ADD CONSTRAINT kpi_services_kpi_services_types_fk FOREIGN KEY (type_id) REFERENCES public.kpi_services_types(id);


--
-- Name: lib_books_covers lib_books_covers_lib_books_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.lib_books_covers
    ADD CONSTRAINT lib_books_covers_lib_books_fk FOREIGN KEY (book_id) REFERENCES public.lib_books(id);


--
-- Name: prop_email prop_email_auth_logins_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_email
    ADD CONSTRAINT prop_email_auth_logins_fk FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: prop_face_detect prop_face_detect_auth_logins_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_face_detect
    ADD CONSTRAINT prop_face_detect_auth_logins_fk FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: prop_proxy_server prop_proxy_server_auth_logins_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.prop_proxy_server
    ADD CONSTRAINT prop_proxy_server_auth_logins_fk FOREIGN KEY (login_id) REFERENCES public.auth_logins(id);


--
-- Name: sk_returns_body sk_returns_body_sk_returns_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.sk_returns_body
    ADD CONSTRAINT sk_returns_body_sk_returns_fk FOREIGN KEY (return_id) REFERENCES public.sk_returns(id);


-- === Directories (справочники) ===

-- acs_DirectionTypes -> acs_direction_types
INSERT INTO acs_direction_types (id, name)
VALUES (1, 'Вход'),
       (2, 'Выход');

-- acs_ReadersTypes -> acs_readers_types
INSERT INTO acs_readers_types (id, name, ports_count)
VALUES (1, 'Hopeland CL7206B7', 1),
       (2, 'Hopeland HF340', 4),
       (3, 'FEIG CPR50', 1);

-- auth_RoleType -> auth_role_type
INSERT INTO auth_role_types (id, name, has_sub_role)
VALUES (1, 'Администратор', false),
       (2, 'Пользователь', false),
       (3, 'Mobile', false);

-- bk_BookStates -> bk_book_states
INSERT INTO bk_book_states (id, name)
VALUES (1, 'На полке'),
       (2, 'Выдана'),
       (3, 'Возвращена'),
       (4, 'Неизвестное место');

-- bk_BookInvStates -> bk_book_inv_states
INSERT INTO bk_book_inv_states (id, name)
VALUES (1, 'Новая'),
       (2, 'Найдена'),
       (3, 'Не найдена'),
       (4, 'Перемещена');

-- dir_Colors -> dir_colors
INSERT INTO dir_colors (id, color_html)
VALUES (1, '#CC4125'),
       (2, '#E06666'),
       (3, '#F6B26B'),
       (4, '#FFD966'),
       (5, '#93C47D'),
       (6, '#76A5AF'),
       (7, '#6D9EEB'),
       (8, '#6FA8DC'),
       (9, '#8E7CC3'),
       (10, '#C27BA0');

-- gpt_Languages -> gpt_languages
INSERT INTO gpt_languages (name, code)
VALUES ('Русский', 'ru-RU'),
       ('Казахский', 'kk-KZ');

-- gpt_VoiceTypes -> gpt_voice_types
INSERT INTO gpt_voice_types (id, name, description)
VALUES (1, 'Мужской голос', 'Глубокий мужской голос для голосового помощника'),
       (2, 'Женский голос', 'Мягкий женский голос для голосового помощника');

-- kpi_ServicesTypes -> kpi_services_types
INSERT INTO kpi_services_types (id, name)
VALUES (1, 'Книговыдача'),
       (2, 'Книговозврат'),
       (3, 'Привязка фото'),
       (4, 'Продление выдачи книг'),
       (5, 'Регистрация читателя в АБИС');

-- main_Modules -> main_modules
INSERT INTO main_modules (id, name)
VALUES (1, 'Мониторинг'),
       (2, 'Ворота'),
       (3, 'SafeKeeper Pro'),
       (4, 'Станции СО'),
       (5, 'Умные полки'),
       (6, 'Настольные считыватели'),
       (7, 'СКУД'),
       (8, 'Библиотекарь'),
       (9, 'Хранение фонда'),
       (10, 'Отчёты'),
       (11, 'Распознавание лиц'),
       (12, 'Пользователи'),
       (13, 'Параметры'),
       (14, 'Активность персонала'),
       (15, 'Мини-библиотека Pro'),
       (16, 'Умный ассистент');

-- mon_DeviceCFGTypes -> mon_device_cfg_types
INSERT INTO mon_device_cfg_types (id, name)
VALUES (1, 'NONE'),
       (2, 'CARDMAN'),
       (3, 'FEIG_MR100'),
       (4, 'FEIG_CPR30'),
       (5, 'RRHF'),
       (6, 'MICROEM'),
       (7, 'RADEL'),
       (8, 'ANDEA'),
       (9, 'Z2 U'),
       (10, 'ACS'),
       (11, 'RES'),
       (12, 'RES'),
       (13, 'RES'),
       (14, 'RR UHF'),
       (15, 'BOOKOS UHF'),
       (16, 'RRU9816'),
       (17, 'FEIG U102'),
       (18, 'CHAINWAY R3');

-- mon_DeviceTypes -> mon_device_types
INSERT INTO mon_device_types (id, name, device_cfg_type_id)
VALUES (1, 'Устройство книговыдачи UniBook / (S) / Reader (S)', 6),
       (2, 'Универсальная станция книговыдачи/программирования меток UniBook', 3),
       (3, 'Устройство книговыдачи UniBook HF RFID 2.0', 7),
       (4, 'Устройство книговыдачи IDlogic EasyBook HF RFID 2.1', 2),
       (5, 'Настольный считыватель UniBook HF/UHF', 1),
       (6, 'Настольный RFID UHF ридер RRU1861DK3324/USB', 14),
       (7, 'Настольное устройство кардридер UniBook Card Reader HF', 6),
       (8, 'Настольное устройство кардридер UniBook Card Reader UHF', 16),
       (9, 'Считыватель бесконтактных смарт-карт ACR1252U-A1', 10),
       (10, 'Кардридер IronLogic Z-2 USB', 9),
       (14, 'Считыватель настольный RST-BOOKOS', 15),
       (15, 'FEIG Настольный ридер ID CPR40.30-USB', 4),
       (16, 'Мультичастотный настольный считыватель UniBook (К)', 1),
       (17, 'Настольный считыватель 3M', 11),
       (18, 'Мультичастотный настольный считыватель R3', 18);

-- mon_EventTypes -> mon_event_types
INSERT INTO mon_event_types (id, name)
VALUES (1, 'Мониторинг ОК'),
       (2, 'Мониторинг Ошибки'),
       (3, 'Статистика');

-- mon_Events -> mon_events
INSERT INTO mon_events (id, name, event_type_id, is_single_event)
VALUES (1, 'RFID-кардридер подключен', 1, 0),
       (2, 'Ворота подключены', 1, 0),
       (3, 'Cортировщик подключен', 1, 0),
       (4, 'Ридер умной полки подключен', 1, 0),
       (5, 'Открытие передней панели станции (для сервисного обслуживания DropBox)', 1, 1),
       (6, 'Отмена заказа в мастер-режиме (SafeKeeper)', 1, 1),
       (7, 'Сброс хранения(очистка ячейки) в мастер-режиме (SafeKeeper)', 1, 1),
       (8, 'Доступ читателю отклонен', 3, 1),
       (9, 'Вход в формуляр', 3, 1),
       (10, 'Выдача книги', 3, 1),
       (11, 'Возврат книги', 3, 1),
       (12, 'Возврат книги через полку', 3, 1),
       (13, 'Продление книги', 3, 1),
       (14, 'Печать чека', 3, 1),
       (15, 'Книгу взяли с полки', 3, 1),
       (16, 'Книгу вернули на полку', 3, 1),
       (17, 'Корзина переполнена', 3, 0),
       (18, 'Корзина очищена', 3, 0),
       (19, 'Камера хранения. Вещи сданы', 3, 1),
       (20, 'Камера хранения. Вещи выданы', 3, 1),
       (21, 'Бронеполка. Заказ выдан', 3, 1),
       (22, 'Мастер. Заказ укомплектован', 3, 1),
       (23, 'Начало сессии читателя', 3, 1),
       (24, 'Начало сессии администратора (мастер-ключ)', 3, 1),
       (25, 'Читатель не найден', 3, 1),
       (26, 'Конец сессии читателя', 3, 1),
       (27, 'Ошибка RFID-считывателя', 2, 0),
       (28, 'Ошибка подключения к АБИС', 2, 0),
       (29, 'Ошибка подключения к контроллеру', 2, 0),
       (30, 'RFID-считыватель подключен', 1, 0),
       (31, 'АБИС подключено', 1, 0),
       (32, 'Контроллер подключен', 1, 0),
       (33, 'Ошибка RFID кардридера', 2, 0),
       (34, 'Ошибка контроллера', 2, 0),
       (35, 'Ошибка сортировщика', 2, 0),
       (36, 'Ошибка подключения к воротам', 2, 0),
       (37, 'Ошибка получения данных со счетчика посетителей', 2, 0),
       (38, 'Ошибка чтения журнала ворот', 2, 0),
       (39, 'Ошибка подключения к камере', 2, 0),
       (40, 'Ошибка подключения к ридеру умной полки', 2, 0),
       (41, 'Шторка книгоприемника заблокирована(Smart|DropBox)', 2, 0),
       (42, 'Принтер подключен', 1, 0),
       (43, 'Ошибка принтера', 2, 0),
       (44, 'Закончилась бумага в принтере', 2, 0),
       (45, 'Мастер. Открытие ячейки', 3, 1),
       (46, 'Мастер. Открытие всех ячеек', 3, 1),
       (47, 'Корзина отсутствует (SmartBox|DropBox без сортировщика)', 2, 0),
       (48, 'Конец сессии администратора', 3, 1),
       (49, 'Рейтинг книги', 3, 1),
       (50, 'Книга в ячейке вендинга', 3, 1),
       (51, 'Книгу забрали из ячейки вендинга', 3, 1),
       (52, 'ООшибка выдачи книги в вендинге', 2, 1),
       (53, 'Запрос местоположения книги', 1, 1),
       (54, 'Поиск книги в хранении фонда', 1, 1),
       (55, 'Вендинг. ПО запущено', 2, 1),
       (56, 'Вендинг. Общие ошибки', 2, 1),
       (57, 'Вендинг. Предупреждение', 2, 1),
       (58, 'Взаимодействие с голосовым ассистентом', 2, 1);

-- mon_GateTypes -> mon_gate_types
INSERT INTO mon_gate_types (id, name)
VALUES (1, 'Защитная RFID-cистема LSG405 HF'),
       (2, 'Защитная RFID-система UniBook Defender'),
       (3, 'Защитная RFID-система UniBook Cinema HF'),
       (4, 'Защитная RFID-система IDlogic EasyBook Sense Gate'),
       (5, 'Защитная RFID-система UniBook Security System UHF');

-- mon_GateCFGTypes -> mon_gate_cfg_types
INSERT INTO mon_gate_cfg_types (id, name, type_id)
VALUES (1, 'Andea LSG405', 1),
       (2, 'FEIG LR2500', 3),
       (3, 'FEIG LRU1002', 4),
       (4, 'TagSys LSP-3', 2),
       (5, 'FEIG LRU3000 UHF', 5),
       (6, 'Impinj', 5),
       (7, 'CLOU (Hopeland)', 5),
       (8, 'Motorola FX7400', 5),
       (9, 'Chainway UR4', 5);

-- mon_ServiceTypes -> mon_service_types
INSERT INTO mon_service_types (id, name)
VALUES (1, 'Служба FaceDetect'),
       (2, 'SIP2 & Email'),
       (3, 'Extra RFID Cards'),
       (4, 'Служба подсчёта посетителей Monogate'),
       (5, 'Служба противокражных ворот (FEIG, ANDEA)'),
       (6, 'Служба противокражных ворот (TAGSYS)'),
       (7, 'Служба BiblioPro'),
       (8, 'Служба турникетов FaceDetect'),
       (9, 'Служба турникетов RFID Core');

-- mon_StationTypes -> mon_station_types
INSERT INTO mon_station_types (id, name)
VALUES (1, 'SmartStand'),
       (2, 'SmartBox'),
       (3, 'SmartShelf'),
       (4, 'SafeKeeper'),
       (5, 'BookDrop'),
       (6, 'Mini'),
       (7, 'SelfReturn'),
       (8, 'SmartShelf + Книговыдача'),
       (9, 'Станция вендинга');

-- sk_EventTypes -> sk_event_types
INSERT INTO sk_event_types (id, name)
VALUES (1, 'Камера хранения. Вещи сданы'),
       (2, 'Камера хранения. Вещи выданы'),
       (3, 'Бронеполка. Заказ выдан'),
       (4, 'Мастер. Заказ укомплектован'),
       (5, 'Мастер. Открытие ячейки'),
       (6, 'Мастер. Открытие всех ячеек'),
       (7, 'Мастер. Книга в ячейке вендинга'),
       (8, 'Книгу забрали из ячейки вендинга'),
       (9, 'Возврат книги в ячейку'),
       (10, 'Мастер. Возвращённые книги забрали из ячейки');

-- sk_OrderStates -> sk_order_states
INSERT INTO sk_order_states (id, name)
VALUES (1, 'Создан'),
       (2, 'Подготовлен'),
       (3, 'Укомплектован'),
       (4, 'Выдан'),
       (5, 'Отменён'),
       (6, 'Выдан с ошибками');

-- sk_SafeKeeperTypes -> sk_safe_keeper_types
INSERT INTO sk_safekeeper_types (id, name)
VALUES (1, 'Гардероб'),
       (2, 'Камера хранения'),
       (3, 'Полка бронирования'),
       (4, 'Камера хранения + бронирование'),
       (5, 'Станция вендинга');

-- dir_hoursDay -> dir_hours_day
INSERT INTO dir_hours_day (hour_value)
VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12),
       (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23);

-- dir_Days -> dir_days (заполнение через generate_series)
INSERT INTO dir_days (day_value)
SELECT generate_series('2020-01-01'::date, '2029-12-31'::date, '1 day'::interval)::date;

-- === StartPack (владелец, библиотека, admin) ===

-- 1. Создаем запись в internal для локального сервера
INSERT INTO public.internal (
    payment_date, 
    payment_demo_mode, 
    name_text, 
    "comment", 
    firm_id, 
    is_reader_for_internal
) VALUES (
    CURRENT_TIMESTAMP,
    1,
    'Локальный сервер',
    'Автоматически созданный стартовый пакет',
    1,
    false
);

INSERT INTO public.owners (
    "name", 
    address, 
    "version", 
    ts1, 
    internal_id, 
    firm_id, 
    is_use_ekp
) 
SELECT 
    'ЦБС',
    'Адрес централизованной библиотечной системы',
    1,
    'ЦБС',
    id,
    1,
    true
FROM public.internal 
WHERE name_text = 'Локальный сервер';

INSERT INTO public.lib_libraries (
    "name",
    fullname,
    manager,
    phone,
    address,
    tag_service_count,
    owner_id,
    ts,
    firm_id,
    "comment"
) 
SELECT 
    'Библиотека',
    'Центральная библиотека',
    'Администратор',
    '+7-XXX-XXX-XX-XX',
    'Адрес библиотеки',
    0,
    id,
    0,
    1,
    'Автоматически созданная библиотека'
FROM public.owners 
WHERE name = 'ЦБС';


DO $$
DECLARE
    owner_id_val uuid;
    salt_val text;
    pass_md5_val text;
    admin_id int;
BEGIN
    -- Получаем ID владельца
    SELECT id INTO owner_id_val FROM public.owners WHERE name = 'ЦБС';
    
    -- Генерируем salt
    salt_val := replace(gen_random_uuid()::text, '-', '');
    
    -- Создаем MD5 хеш
    pass_md5_val := upper(md5(salt_val || 'admin'));
    
    -- Вставляем пользователя
    INSERT INTO public.auth_logins (
        owner_id,
        login,
        pass_md5,
        pass_salt,
        first_name,
        second_name,
        last_name,
        date_created,
        expired,
        is_deleted,
        stamp
    ) VALUES (
        owner_id_val,
        'admin',
        pass_md5_val,
        salt_val,
        'Администратор',
        'Администратор',
        'Администратор',
        CURRENT_TIMESTAMP,
        CURRENT_TIMESTAMP + interval '10 years',
        false,
        E'\\x0000000000000001'
    ) RETURNING id INTO admin_id;
    
    -- Добавляем роль
    INSERT INTO public.auth_login_roles (
        login_id,
        role_type_id,
        role_sub_id
    ) VALUES (
        admin_id,
        1,
        -1
    );
    
END $$;

-- === License (полный доступ ко всем модулям) ===

-- Полная лицензия (все модули из ModulesAccessDTO / VersionWorker.CheckVersion).
-- Алгоритм должен совпадать с Service/Utils/License/VersionWorker.cs:
--   version (int32) = ((байт_15 XOR байт_4) << 24) | 0x7FFF
--   байты берутся из UUID владельца без дефисов, позиции как в C# Substring(14,2) и Substring(4,2) — hex.
-- 0x7FFF = 15 младших бит включены (все флаги модулей).
--
-- Обновляет owners.version для всех владельцев (после StartPack — для записи «ЦБС» и любых других).

UPDATE public.owners o
SET version = sub.v
FROM (
    SELECT
        o2.id,
        (
            CASE
                WHEN (((bytes.b1 # bytes.b2)::bigint << 24) | 32767) > 2147483647
                THEN (((bytes.b1 # bytes.b2)::bigint << 24) | 32767) - 4294967296
                ELSE (((bytes.b1 # bytes.b2)::bigint << 24) | 32767)
            END
        )::integer AS v
    FROM public.owners o2
    CROSS JOIN LATERAL (
        SELECT
            get_byte(decode(substr(upper(replace(o2.id::text, '-', '')), 15, 2), 'hex'), 0) AS b1,
            get_byte(decode(substr(upper(replace(o2.id::text, '-', '')), 5, 2), 'hex'), 0) AS b2
    ) bytes
) sub
WHERE o.id = sub.id;


-- === DemoSeed (демо-устройства, станции, СКУД) ===

-- Один оператор DO (Npgsql 4: несколько команд через «;» в одном CommandText могут выполняться неполно).
DO $$
DECLARE
  v_library_id uuid;
  v_owner_id uuid;
  v_entrance_id integer;
  v_zone_id integer;
  v_reader_id integer;
  v_fd_device_id integer;
  v_camera_id integer;
  v_gate_id uuid;
  v_station_self uuid;
  v_station_shelf uuid;
  v_station_skp uuid;
  v_station_mini uuid;
BEGIN
  SELECT id INTO v_library_id FROM public.lib_libraries ORDER BY date_created LIMIT 1;
  SELECT id INTO v_owner_id FROM public.owners ORDER BY date_created LIMIT 1;
  IF v_library_id IS NULL OR v_owner_id IS NULL THEN
    RAISE EXCEPTION 'DemoSeed: не найдена библиотека или владелец (нужен стартовый пакет).';
  END IF;

  DELETE FROM acs_reader_antennas WHERE name = 'Демо: антенна СКУД';
  DELETE FROM acs_readers WHERE name = 'Демо: считыватель СКУД';
  DELETE FROM acs_zones WHERE name = 'Демо: зона СКУД';
  DELETE FROM acs_entrances WHERE name = 'Демо: проход СКУД';

  DELETE FROM monitor_devices WHERE name = 'Демо: настольный считыватель';

  DELETE FROM sk_safekeepers WHERE id IN (
    SELECT id FROM public.mon_stations WHERE sn IN ('DEMO-SEED-SKP', 'DEMO-SEED-MINI', 'DEMO-SEED-BOOK')
  );
  DELETE FROM public.mon_stations WHERE sn IN (
    'DEMO-SEED-SELF',
    'DEMO-SEED-SHELF',
    'DEMO-SEED-SKP',
    'DEMO-SEED-MINI',
    'DEMO-SEED-BOOK'
  );

  DELETE FROM fd_device_cameras WHERE name = 'Демо: камера бронирования';
  DELETE FROM fd_devices WHERE hardware_id = 'DEMO-FD-BOOKING';

  DELETE FROM mon_devices WHERE library_id = v_library_id AND name IN (
    'Демо: станция самообслуживания',
    'Демо: умная полка',
    'Демо: мини-библиотека',
    'Демо: настольный считыватель'
  );

  DELETE FROM mon_gates WHERE name = 'Демо: ворота' AND library_id = v_library_id;

  DELETE FROM rdr_readers WHERE abis_code = 'DEMO-READER-001';

  INSERT INTO mon_gates (name, ip, port, type_id, cfg_type_id, library_id)
  VALUES ('Демо: ворота', '127.0.0.1', 10001, 1, 1, v_library_id)
  RETURNING id INTO v_gate_id;

  INSERT INTO fd_devices (hardware_id, ip, port, login, password, library_id)
  VALUES ('DEMO-FD-BOOKING', '127.0.0.1', 80, 'admin', 'admin', v_library_id)
  RETURNING id INTO v_fd_device_id;

  INSERT INTO fd_device_cameras (name, device_id, channel_no, ip)
  VALUES ('Демо: камера бронирования', v_fd_device_id, 1, '127.0.0.1')
  RETURNING id INTO v_camera_id;

  -- 1. Станция СО — TypeID = 1
  v_station_self := gen_random_uuid();
  INSERT INTO public.mon_stations (id, name, sn, station_type_id, library_id)
  VALUES (v_station_self, 'Демо: станция самообслуживания', 'DEMO-SEED-SELF', 1, v_library_id);

  -- 2. SafeKeepers Pro — TypeID = 4, sk_safekeepers SafekeeperTypeID = 4, камера бронирования
  v_station_skp := gen_random_uuid();
  INSERT INTO public.mon_stations (id, name, sn, station_type_id, library_id, device_camera_id)
  VALUES (v_station_skp, 'Демо: Камера бронирования', 'DEMO-SEED-SKP', 4, v_library_id, v_camera_id);

  INSERT INTO public.sk_safekeepers (id, cells_count, ip_address, safe_keeper_type_id, cell_shift, parent_id)
  VALUES (v_station_skp, 16, '127.0.0.1', 4, 0, NULL);

  -- 3. Мини-библиотека — TypeID = 9, sk_safekeepers SafekeeperTypeID = 5
  v_station_mini := gen_random_uuid();
  INSERT INTO public.mon_stations (id, name, sn, station_type_id, library_id)
  VALUES (v_station_mini, 'Демо: мини-библиотека', 'DEMO-SEED-MINI', 9, v_library_id);

  INSERT INTO public.sk_safekeepers (id, cells_count, ip_address, safe_keeper_type_id, cell_shift, parent_id)
  VALUES (v_station_mini, 3, '127.0.0.1', 5, 0, NULL);

  -- 4. Умные полки — TypeID = 3
  v_station_shelf := gen_random_uuid();
  INSERT INTO public.mon_stations (id, name, sn, station_type_id, library_id)
  VALUES (v_station_shelf, 'Демо: умная полка', 'DEMO-SEED-SHELF', 3, v_library_id);

  INSERT INTO mon_devices (name, type_id, library_id)
  VALUES ('Демо: настольный считыватель', 5, v_library_id);

  INSERT INTO acs_entrances (name, library_id)
  VALUES ('Демо: проход СКУД', v_library_id)
  RETURNING id INTO v_entrance_id;

  INSERT INTO acs_zones (name, entrance_id, direction_type_id)
  VALUES ('Демо: зона СКУД', v_entrance_id, 1)
  RETURNING id INTO v_zone_id;

  INSERT INTO acs_readers (name, ip_address, port, entrance_id, reader_type_id)
  VALUES ('Демо: считыватель СКУД', '127.0.0.1', 9001, v_entrance_id, 1)
  RETURNING id INTO v_reader_id;

  INSERT INTO acs_reader_antennas (reader_port_number, name, zone_id, reader_id)
  VALUES (1, 'Демо: антенна СКУД', v_zone_id, v_reader_id);

  INSERT INTO rdr_readers (name, abis_code, rfid_code, owner_id, comment)
  VALUES ('Демо читатель', 'DEMO-READER-001', 'DEMO-RFID-001', v_owner_id, 'Создано установщиком демо-версии');

  RAISE NOTICE 'DemoSeed: OK library=%, gate=%, stations=CO/SKP/mini/shelf', v_library_id, v_gate_id;
END $$;

