# Листинг
Листинг - является основополагающей настройкой при работе с таблицами/моделями, в Конструкторе. То есть, отображение списка данных (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}/ - данные, для отображения на странице листинга