# Листинг

Листинг - является основополагающей настройкой при работе с таблицами/моделями, в Конструкторе. То есть, отображение списка данных (rows), с набором полей (columns), исходя из заданных условий (where, permissions and etc). На его основе, строится вся работа с объектами в Конструкторе (row, crud, raw-sql).

Пример иерархии:

Листинг - вывод списка объектов
    Детали - вывод деталей объекта
        CRUD - действия над объектом
    Raw SQL - нестандартные sql запросы

Для регистрации таблицы/модели, необходимо добавить вводную информацию в панели администратора /admin/:

Конструктор -> Листинг -> Добавить запись

Далее указать Наименование в логически верном виде:

{Кабинет или Cайт} - {Модель/Таблица} - {Подмодуль и тд}

Пример: 
    Кабинет - Конкурсы
    Кабинет - Конкурсы - История

В качестве Ключа, на английском языке и с маленькой буквы, ввести название таблицы в БД или произвольный api endpoint.

Обозначить Категорию, для простоты фильтрации и отображения в админке Конструктора. Это необходимо для того чтобы структурировать большое кол-во различных листингов по логической составляющей.

В итоге перейти к настройке полей, которые необходимы для работы фронтенда.

# Поля

Список полей таблицы/модели, через запятую. Для вывода значений из связанной таблицы по Foreign Key, используйте {current-field}__{fk-field}.

Пример:

category__name

Внимание

Не используйте префикс языка, так как при смене языка в профиле, значение корректного языка подставляться не будет

# Фильтры

Список полей типа ManyToMany/ForeignKey/Boolean/Date/DateTime, для фильтрации по заданому значению. Фильтры для Фронтенда возвращаются в Meta информации листинга.

Пример:

"filter_fields": {
    "status": {
        "type": "foreignkey",
        "name": "Статус",
        "data": {
            "ACTIVE": "Активный",
            "PUBLISHED": "Опубликованный",
            "REMOVED": "Удален",
            "DRAFT": "Черновик"
        }
    },
    "kpi": {
        "type": "manytomany",
        "name": "kpi",
        "data": {
            "KOLICHESTVO_OPUBLIKOVANNYH_NAUCHNYH_PUBLIKACIJ": "Количество опубликованных научных публикаций",
            "KOLICHESVO_PATENTOV": "Кол-во патентов"
        }
    },
    "pre_close_date": {
        "type": "datetime",
        "name": "Дата закрытия коротких заявок"
    }
},
...

Совет

Фильтры, которые являются системными, для вывода внутри детальных страниц, должны заканчиваться постфиксом _sys, для того что бы не выводить фильтр не в подходящем месте.

# Поиск

Список текстовых полей, по которым будет осуществляться поиск пользователем системы.

# Сортировка

Список полей с индексом в БД, для сортировки списка объектов. -id - это DESC, id - это ASC.

# Права

Обязательное поле, которое включает в себя функцию проверки прав доступа к листингу.

Пример:

CREATE OR REPLACE FUNCTION {model}_permissions (action_name char, listing_id int, user_organization_id int, user_role char, user_id int, document_id character, params jsonb)
    RETURNS bool
    LANGUAGE plpgsql
    AS $$
DECLARE
    --
    -- Проверка прав доступа к листингу. Доступ только для авторизированных.
    -- URL: /admin/processing/listingpage/{id}
    --
BEGIN
    IF user_id > 0 THEN
        RETURN TRUE;
    ELSE
        RETURN FALSE;
    END IF;
END;
$$;

# Основные действия

Отображаются на странице листинга в верхней части, как массовые действия над всеми объектами таблицы/модели.

Пример:

Удалить все
Архивировать все
Ссылка на отчет
Другая ссылка

За корректный вывод действий, как и в Деталях, отвечает отдельная функция. По умолчанию: default_listing_base_actions. В случае необходимости, можно перегрузить логику работы функции с учетом прав и роли пользователя.

Совет

Полный пример реализации функции и настройки Действий, доступен в разделе Object:Details.

# Sub-listing в табах и работа с GET параметрами

В некоторых случаях, листинг встраивается в табы, и используется для вывода таблиц с данными, над которыми необходимо произвести действия. Пример реализации экшенов, в подобного рода случаях:

CREATE OR REPLACE FUNCTION public.sub_listing_actions(action_name character, listing_id integer, user_organization_id integer, user_role character, user_id integer, user_language character, document_id character, params jsonb)
  RETURNS SETOF processing_listing_actions
  LANGUAGE plpgsql
AS $function$
DECLARE
    r processing_listing_actions%rowtype;
    in_sub_app text;
BEGIN

    if jsonb_extract_path(params, 'sub_app') is null then in_sub_app := 0; else in_sub_app := params ->> 'sub_app'; end if;

    FOR r IN
        SELECT
            *
        FROM
            processing_listing_actions
        WHERE
            listing_page_id = listing_id
        AND
            user_role IN ('SOME_ROLE')
            AND
            (
                SELECT
                        COUNT(*)
                FROM sub_apps
                WHERE
                      status_id = 'SOME_STATUS'
                        AND
                      id = in_sub_app::int
            ) > 0
    LOOP
        RETURN NEXT r;
    END LOOP;
    RETURN;
END;
$function$

# Экстра значения (SELECT)

В случае, когда необходимо что-то динамически посчитать или взять значение из другой таблицы, можно воспользоваться возможностью подстановки собственных подзапросов, или же вызовом функций.

Пример:

Поле: PI_name
Функция или запрос: (SELECT fullname FROM application_teams at2  WHERE application_id = applications.id AND member_type_id = 'PI' LIMIT 1)

или

Поле: has_application
Функция или запрос: def_create_app({{ user.id }}, applications.id)

Реализация функции:

CREATE OR REPLACE FUNCTION public.def_create_app(user_id integer, cont_id integer)
 RETURNS integer
 LANGUAGE plpgsql
AS $function$
DECLARE
    --
    -- Функция для определения разрешения на создания заявки
    -- 
    --
BEGIN
	if 	(select count(*) from applications a where a.created_by_id = user_id and a.contest_id = cont_id) = 0 
	then return 0;
	else return 1;
	end if;
END;
$function$

# Фильтры листинга (WHERE)

Иногда необходимо отобразить листинг, в зависимости от разных факторов.

# Пример фильтрации листинга, на основе статуса и роли пользователя.

Если не APPLICANT, вывести все записи
Если APPLICANT, вывести во всех статусах кроме DRAFT и ACTIVE

Поле для фильтрации: status_id
Модификатор: IN
Значение: (SELECT id FROM filter_contest_listing({{ user.id }}, '{{ user.role_id }}'))

Пример функции:

CREATE OR REPLACE FUNCTION public.filter_contest_listing(user_id integer, user_role character)
  RETURNS TABLE(id character)
  LANGUAGE sql
AS $function$
    --
    -- Фильрация листинга Конкурсов.
    -- /admin/processing/listingpage/9/change/
    --
    
    SELECT
        code
    FROM 
        reference_contest_statuses
    WHERE
    (
        user_role NOT IN ('APPLICANT')
    )
    -- Для заявителя в реестре конкурсов не должно быть видно конкурсы со статусом DRAFT и ACTIVE
    OR
    (
        user_role IN ('APPLICANT') AND code NOT IN ('DRAFT', 'ACTIVE')
    )
    ;
$function$

# Пример фильтрации листинга, на основании автора

Если не APPLICANT, вывести все записи в таблице
Если APPLICANT, вывести записи, где пользователь является автором

Поле для фильтрации: created_by_id  
Модификатор: IN  
Значение: (SELECT id FROM filter_application_listing({{ user.id }}, '{{ user.role_id }}'))  

Пример функции:

CREATE OR REPLACE FUNCTION public.filter_application_listing(user_id integer, user_role character)
  RETURNS TABLE(id integer)
  LANGUAGE sql
AS $function$
    --
    -- Фильрация листинга
    -- /admin/processing/listingpage/20/change/
    --
    SELECT
        id
    FROM
        authentication_user
    WHERE
    (
        user_role IN ('PIU_DIRECTOR', 'COORDINATOR','INDEPENDENT_EXPERT', 'INTERNATIONAL_EXPERT_COUNCIL', 'TECHNICAL_EXPERT') AND id > 0
    )
    OR
    (
        user_role IN ('APPLICANT') AND id = user_id
    )
    ;
$function$

# Действия над объектом листинга

Это экшены, которые отображаются непосредственно в листинге, напротив каждой записи таблицы. Функционал дублирует логику действий, которые реализованы в деталях, за исключением фильтрации самих экшенов. Отображать те или иные действия, можно подставляя поле в Экстра значения (SELECT) значение true/false для кнопки.

# API эндпоинты (Фронтенд)

/api/v1/processing/list/meta/{listing-slug}/ - мета-данные, для автоматического формирования страницы листинга
/api/v1/processing/list/{listing-slug}/ - данные, для отображения на странице листинга