# FILTER — Фильтр таблицы

Фильтр — это сохранённый набор условий отбора записей в таблице (например «Только активные», «Мои сделки»). Фильтр всегда привязан к одной таблице.

Фильтрами управляешь так же, как остальными элементами конфигурации — через инструмент set_result (операции create / update / delete). Отдельных инструментов для фильтров нет.

# Создание фильтра

# ПЕРЕД созданием фильтра: правило «Все записи»
Прежде чем создать НОВЫЙ фильтр, проверь, есть ли в таблице фильтры:
вызови get_list с type=FILTER и parent_id = id таблицы.
- Если в таблице УЖЕ есть хотя бы один фильтр — просто создавай запрошенный фильтр, ничего дополнительно не делай.
- Если фильтров НЕТ (список пустой) И запрошенный фильтр содержит условия — сначала создай служебный фильтр «Все записи» (без условий), и только потом запрошенный. «Все записи» должен идти ПЕРВЫМ.
- Если фильтров нет, но сам запрошенный фильтр и есть «показать все записи» (без условий) — отдельный «Все записи» не нужен, он сам станет первым.
«Все записи» — это единственный случай, когда фильтр создаётся БЕЗ условий
(во всех остальных случаях у фильтра должно быть хотя бы одно условие).
Создаётся так: cond_set передаётся пустым массивом [] — это значит «без условий, показывать все записи».
Доступ для «Все записи» выдаётся как в КБ: группе администраторов (group_id = 1) —
просмотр (VIEW = 1) и пометка фильтром по умолчанию (DEFAULT = 1; это def_use,
с ним «Все записи» открывается при входе в таблицу).
Это единственный фильтр, которому доступ задаём явно; для остальных — доступы как запросил пользователь.
Обе операции отправляй в ОДНОМ вызове set_result, в массиве операций «Все записи» — первым элементом.
Пример (в таблице 42 фильтров ещё нет, пользователь просит фильтр «Крупные»):
{"result":[
  {"operation":"create","type":"FILTER",
    "access":[{"group_id":1,"VIEW":1,"DEFAULT":1}],
    "props":{"table_id":42,"name":"Все записи","cond_set":[]}},
  {"operation":"create","type":"FILTER","access":[],
   "props":{"table_id":42,"name":"Крупные","cond_set":[
     {"field":"Сумма","op":">","value":"1000"}
   ]}}
]}

Условия отбора передавай в props.cond_set как массив условий.

- table_id - ID таблицы (узнать поля можно через get_item type=TABLE).
- name - короткое название фильтра (не длиннее 20 символов).
- cond_set - массив условий. Поля указывай по их названию в таблице.
- access - при создании обязателен.
  - Пустой массив [] = доступ по умолчанию (фильтр видит администратор; другим группам не виден). Используй [], если пользователь не просил открыть доступ конкретным группам.
  - Чтобы открыть фильтр группе — задавай явно: access:[{"group_id":<id>,"VIEW":1}] (добавь "DEFAULT":1, если он должен быть фильтром по умолчанию).
  - ID групп — через get_list type=GROUP (администраторы = group_id=1).
- Поддерживаемые операторы (op): =, !=, >, <, >=, <=, contains (содержит подстроку), not_contains (не содержит подстроку), empty (пусто), not_empty (не пусто).
- ВАЖНО про текст и подстроки: если нужно отобрать записи, в значении которых ЕСТЬ заданный фрагмент текста — используй contains; если в значении НЕ должно быть фрагмента ("в имени не было буквы р", "название не содержит ООО") — используй not_contains, а НЕ !=. Оператор != ('не равно') отсекает только точное совпадение со всей строкой, а не наличие подстроки. Оператор = используй только когда значение должно совпадать целиком.
- union - связка со СЛЕДУЮЩИМ условием (AND/OR). У последнего условия union не указывается. Если все условия должны выполняться вместе - используй AND.
- В value можно использовать плейсхолдер {current} (текущий пользователь) для полей-пользователей.

Пример: «активные записи с суммой больше 1000» →
set_result, операция:
{"operation":"create","type":"FILTER","access":[],
 "props":{"table_id":42,"name":"Крупные","cond_set":[
   {"field":"Статус","op":"=","value":"0","union":"AND"},
   {"field":"Сумма","op":">","value":"1000"}
 ]}}

Ограничения текущей версии: вложенные фильтры и условия со скобками пока не поддерживаются.
Операторы empty и not_empty (проверка на пустоту) нельзя редактировать в визуальном конструкторе — фильтр с ними откроется только в режиме эксперта. Если важно, чтобы пользователь мог править фильтр в конструкторе, не используй empty/not_empty.

# Архивация фильтра
Архивировать фильтр — это НЕ только arc. В системе архивный фильтр определяется ДВУМЯ полями сразу:
- arc=1 — убирает фильтр из списка настроек фильтров (переносит в раздел «Архив»);
- disabled=1 — убирает фильтр со вкладок на самой таблице.
Если выставить только arc=1, фильтр уйдёт в архив в настройках, но ОСТАНЕТСЯ вкладкой на таблице. Поэтому:
- «убрать в архив» / «заархивировать» → операция update с props: {"arc":1, "disabled":1}
- «вернуть из архива» / «разархивировать» → операция update с props: {"arc":0, "disabled":0}

# Изменение существующего фильтра

ВАЖНОЕ ПРАВИЛО: если пользователь просит ИЗМЕНИТЬ/ПОПРАВИТЬ/ПЕРЕДЕЛАТЬ уже существующий фильтр — используй операцию update с id этого фильтра. НИКОГДА не создавай при этом новый фильтр. Создание нового фильтра (операция create) допустимо только когда пользователь явно просит создать НОВЫЙ фильтр.

При update условия фильтра заменяются ПОЛНОСТЬЮ:
- В props.cond_set передавай весь нужный набор условий целиком.
- name можно передать прежний.

Пример:
{"operation":"update","type":"FILTER","id":128,
 "props":{"name":"Крупные","cond_set":[
   {"field":"Сумма","op":">","value":"5000"}
 ]}}

Если ты не знаешь id нужного фильтра:
1. Вызови get_list с type=FILTER и parent_id = id таблицы, чтобы получить список фильтров таблицы.
2. Найди нужный фильтр по названию и возьми его id.
3. Передай этот id в операцию update.
Если не удаётся однозначно определить, какой фильтр менять — уточни у пользователя, не меняй наугад.

# Добавление / удаление / изменение одного условия (не теряя остальные)

cond_set заменяется ПОЛНОСТЬЮ. Поэтому чтобы ДОБАВИТЬ условие (а не переписать фильтр с нуля):
1. Вызови get_item с type=FILTER и id фильтра — в props.cond_set вернётся текущий массив условий.
2. Если cond_set = null — фильтр экспертный (нет визуальных условий) и по условиям не читается. Сообщи это пользователю и предложи задать набор условий заново.
3. Иначе возьми массив cond_set из ответа, ДОБАВЬ в него новое условие (или измени/удали нужное) и передай ВЕСЬ массив обратно операцией update с тем же id.

Про union при добавлении: union у условия — это связка со СЛЕДУЮЩИМ условием (AND/OR), у последнего условия union нет. Если дописываешь условие в конец — проставь union (AND или OR) у того условия, которое раньше было последним, а у нового (теперь последнего) union не указывай.

- Удалить условие: получи cond_set через get_item, УБЕРИ ненужное условие из массива и отправь оставшиеся операцией update.
- Изменить условие: получи cond_set, поменяй нужный элемент (field/op/value) и отправь весь массив обратно.
Нельзя удалить ВСЕ условия — у фильтра должно остаться хотя бы одно. Если пользователь хочет убрать единственное условие, это означает удаление самого фильтра (операция delete), а не условия.
 Исключение — служебный фильтр «Все записи» (см. раздел в начале): он создаётся без условий с пустым cond_set.

# Поля-связи (тип «Связь» на другую таблицу)
По полю-связи можно фильтровать операторами = (равно записи) и != (не равно записи).
- В value укажи НАЗВАНИЕ связанной записи (инструмент сам найдёт её id в связанной таблице) или сразу её числовой id.
- Если по названию находится несколько записей или ни одной — инструмент вернёт ошибку; тогда уточни запись у пользователя или передай её id.
- Множественные связи (когда в поле можно выбрать несколько записей) тоже поддерживаются: = найдёт записи, где среди выбранных есть указанная.
Операторы contains/not_contains/>/< к полю-связи неприменимы.

# Поля-даты
По полю-дате доступны операторы =, !=, >, <, >=, <=. В value укажи:
- конкретную дату 'ГГГГ-ММ-ДД' (например '2026-06-01');
- или относительный период: today (сегодня), now (текущий момент), this_week (текущая неделя), this_half_month (текущая половина месяца), this_month (текущий месяц), this_year (текущий год), empty (пустая дата).
Для периодов (this_week/this_month/this_year/this_half_month) оператор = означает «попадает в этот период», > — «позже периода», < — «раньше периода».
Сдвиг (относительные интервалы): добавь offset (целое со знаком) и offset_period (day/month/year/hour/minute).
- «за последние 7 дней»: value="today", op=">=", offset=-7, offset_period="day".
- «в этом месяце, но через месяц от текущего»: value="this_month", offset=1, offset_period="month".
Без offset сдвиг не применяется.

# Поля-списки (тип «Список»)
- Одиночный список: операторы = и !=. Множественный: contains / not_contains.
- В value передавай ТОЧНЫЙ текст одного из вариантов списка. Если не уверен — посмотри поле через get_item (type=FIELD): там перечислены допустимые значения (list_values). Не выдумывай значение: если оно не совпадёт, инструмент вернёт ошибку со списком допустимых вариантов — выбери из них.

# Поля-пользователи (тип «Пользователь»)
- Одиночное поле: операторы = и !=. Множественное: contains («среди выбранных есть пользователь») и not_contains.
- В value укажи: current (текущий пользователь — для «мои записи»/«мои сделки»), либо точное ФИО (инструмент найдёт id), либо сразу числовой id.
- Если по ФИО находится несколько пользователей или никого — будет ошибка; тогда передай id.

# Поля-группы (тип «Группа»)
- Одиночное поле-группа: операторы = и !=. Множественное: contains / not_contains.
- В value укажи: current (текущая группа пользователя — для «записи моей группы»), либо точное название группы (инструмент найдёт id), либо числовой id.
- Если по названию найдено несколько групп или ни одной — будет ошибка; тогда передай id.

# Свойства (props) фильтра, которые можно читать и безопасно менять:

{
    "table_id": "ID таблицы, к которой относится фильтр (обязательно)",
    "name": "название фильтра",
    "cond_set": "массив условий отбора (см. выше) — задаёт условия фильтра",
    "num": "порядковый номер (позиция в списке фильтров). При создании фильтра не передавай этот параметр, он будет заполнен автоматически.",
    "hidden": "1 - скрыть фильтр из списка, 0 - показывать",
    "show_title": "1 - показывать заголовок фильтра",
    "hide_add_rec": "1 - скрыть кнопку добавления записи под этим фильтром",
    "count_records_on": "1 - показывать количество записей",
    "def_sort": "сортировка по умолчанию, напр. \"int_name_поля desc\"",
    "group_field": "ID поля для группировки (0 - без группировки)",
    "desc_group": "1 - группировка по убыванию",
    "help": "текст подсказки к фильтру",
    "disabled": "1 - фильтр выключен/скрыт со вкладок таблицы (для архивации ставить вместе с arc)",
    "arc": "1 - фильтр в архиве. ВАЖНО: архивация = выставить ОДНОВРЕМЕННО arc=1 И disabled=1; возврат из архива = оба в 0"
}

# Технические поля (НЕ задавать через props):
value, expert, complex, expert_user, pid, exclude_dup, owner_id
— это скомпилированное условие отбора и служебные данные. Они формируются автоматически из cond_set, задавать их напрямую нельзя (попытка установить value/expert вернёт ошибку). Управляй условиями только через cond_set.