# Чтение данных из Первой Формы

Справочник по получению информации через смарт-выражения и смарт-фильтры 1Формы. Описывает атрибуты задач, пользователей, категорий и файлов; работу с дополнительными параметрами всех типов; коллекции, условную логику и типовые запросы. Для администраторов и разработчиков смарт-автоматизаций, настраивающих фильтры, вычисляемые поля и TSQL-запросы.

## Концепция смарт-выражений и смарт-фильтров

Смарт-выражения и смарт-фильтры — SQL-подобные запросы, построенные через конструктор 1Формы. Смарт-фильтры — частный случай смарт-выражений с результатом `true/false` или массивом элементов для фильтрации выборок.

**Смарт-выражения** — SQL-подобные запросы, построенные через конструктор 1Ф. Возвращают значения любых типов.

**Смарт-фильтры** — смарт-выражения, возвращающие `true/false` или массив элементов. Используются для фильтрации выборок.

Оба инструмента могут работать в режимах:

- **SMART** — визуальный конструктор
- **TSQL** — прямой SQL-код

---

## Основные сущности для чтения

### Задача (Task)

Атрибуты задачи, доступные в выражениях:

| Атрибут | Тип | Описание |
|---------|-----|----------|
| ID задачи | `Integer` | Уникальный идентификатор |
| Номер задачи | `Integer` | Номер в категории |
| Текст | `String` | Текст/описание задачи |
| Заказчик | `1F.User` | Пользователь-заказчик |
| Исполнители | `Collection<1F.User>` | Коллекция исполнителей |
| Ответственный исполнитель | `1F.User` | Ответственный |
| Подписчики | `Collection<1F.User>` | Подписчики задачи |
| Категория | `1F.Subcat` | Категория задачи |
| Статус | `1F.State` | Текущий статус |
| Срок | `DateTime` | Срок выполнения |
| Дата начала работ | `DateTime` | Плановое начало |
| Дата создания | `DateTime` | Когда создана |
| Дата закрытия | `DateTime` | Когда закрыта |
| Приоритет | `Integer` | 0=низкий, 1=обычный, 3=высокий |
| Конфиденциальность | `Boolean` | Признак конфиденциальности |

### Пользователь, категория и файл

Атрибуты пользователя:

| Атрибут | Тип | Описание |
|---------|-----|----------|
| ID | `Integer` | Уникальный идентификатор |
| Логин | `String` | Имя входа |
| ФИО | `String` | Полное имя |
| Email | `String` | Адрес почты |
| Телефон | `String` | Номер телефона |
| Группы | `Collection<1F.UserGroup>` | Группы пользователя |
| Орг.единица | `1F.OrgUnit` | Позиция в орг.структуре |
| Часовая зона | `String` | Таймзона |
| Активен | `Boolean` | Признак активности |

Атрибуты категории:

| Атрибут | Тип | Описание |
|---------|-----|----------|
| ID | `Integer` | Уникальный идентификатор |
| Название | `String` | Наименование |
| Описание | `String` | Описание категории |
| Родительский раздел | `1F.Section` | Раздел |

Атрибуты файла:

| Атрибут | Тип | Описание |
|---------|-----|----------|
| FileId | `Integer` | ID файла |
| VersionId | `Integer` | ID версии |
| Name | `String` | Имя файла |
| IsDeleted | `Boolean` | Удалён ли |
| LatestVersionId | `Integer` | Последняя версия |

---

## Работа с дополнительными параметрами (ДП)

### Числовые и строковые ДП

Работа с числовыми ДП:

```
-- Получение значения
ДП.Сумма

-- Арифметика
ДП.Сумма * 1.2
ДП.Цена / ДП.Количество

-- Важно: целочисленное деление
1 / 5 = 0
1.0 / 5 = 0.2
```

**Функции преобразования:**

- `ВСтроку(число)` — число → строка
- `ВЦелоеЧисло(строка)` — строка → целое
- `ВДробноеЧисло(строка)` — строка → decimal
- `Округлить(число)` — округление
- `МинЦелоеНеМеньше(число)` — ceiling
- `МаксЦелоеНеБольше(число)` — floor

Работа со строковыми ДП:

```
-- Конкатенация
ДП.Фамилия + ' ' + ДП.Имя

-- Сравнение
ДП.Статус = 'Активен'

-- Строки в одинарных кавычках
'текст'
```

**Функции работы со строками:**

| Функция | Описание | Пример |
|---------|----------|--------|
| `СтрокаСодержит(стр, подстр)` | Проверка вхождения | `СтрокаСодержит(ДП.Название, 'срочно')` |
| `НачинаетсяС(стр, подстр)` | Начинается ли с | `НачинаетсяС(ДП.Код, 'PRJ-')` |
| `ЗаканчиваетсяНа(стр, подстр)` | Заканчивается ли на | `ЗаканчиваетсяНа(ДП.Email, '@company.ru')` |
| `Подстрока(стр, начало, длина)` | Извлечение подстроки | `Подстрока(ДП.Код, 1, 3)` |
| `ВернутьСлева(стр, N)` | Первые N символов | `ВернутьСлева(ДП.Артикул, 4)` |
| `ВернутьСправа(стр, N)` | Последние N символов | `ВернутьСправа(ДП.Телефон, 4)` |
| `ДлинаСтроки(стр)` | Длина строки | `ДлинаСтроки(ДП.Описание)` |
| `ЗаменитьВСтроке(стр, что, на_что)` | Замена | `ЗаменитьВСтроке(ДП.Текст, 'старое', 'новое')` |
| `ВВерхРегистр(стр)` | ВЕРХНИЙ РЕГИСТР | — |
| `ВНижРегистр(стр)` | нижний регистр | — |
| `БезПробелов(стр)` | Trim | — |
| `СтрокаПуста(стр)` | Проверка на пустоту | — |
| `IsNullString(стр1, стр2)` | Если null → стр2 | `IsNullString(ДП.Комментарий, '')` |

**Поиск в строке:**
```
-- Позиция подстроки (0 если не найдено)
ПоискВСтроке('что', 'где', начальная_позиция)

-- Поиск по шаблону (% = любые символы, _ = один символ)
ПоискПоШаблону('%pattern%', строка)
```

---

### Дата и время

Работа с датами и временем:

```
-- Текущая дата/время
ТекущиеДатаВремя()

-- Создание даты
НовыеДатаВремя(2024, 12, 31, 18, 0, 0)
```

**Извлечение компонентов:**

| Функция | Возвращает |
|---------|-----------|
| `Год(дата)` | Год |
| `Месяц(дата)` | Месяц (1-12) |
| `День(дата)` | День месяца |
| `Час(дата)` | Час (0-23) |
| `Минута(дата)` | Минуты |
| `ДеньГода(дата)` | День в году (1-366) |
| `ДеньНедели(дата)` | Название дня недели |
| `НерабочийДень(дата)` | true/false |

**Арифметика с датами:**

| Функция | Описание |
|---------|----------|
| `ДобавитьДней(дата, N)` | +/- N дней |
| `ДобавитьМесяцев(дата, N)` | +/- N месяцев |
| `ДобавитьЛет(дата, N)` | +/- N лет |
| `ДобавитьЧасов(дата, N)` | +/- N часов |
| `ДобавитьМинут(дата, N)` | +/- N минут |
| `ДобавитьРабочихДней(дата, N)` | +/- N рабочих дней |
| `ДобавитьРабочихЧасов(дата, N)` | +/- N рабочих часов |
| `ДобавитьРабочихМинут(дата, N)` | +/- N рабочих минут |
| `ДобавитьРабочихМинутEx(дата, N)` | +/- N рабочих минут по производственному календарю |
| `ДобавитьРабочихЧасовEx(дата, N)` | +/- N рабочих часов по производственному календарю |
| `ДобавитьРабочихДнейEx(дата, N)` | +/- N рабочих дней по производственному календарю |

**Разница между датами:**

| Функция | Возвращает |
|---------|-----------|
| `РазницаДней(дата1, дата2)` | Целых дней |
| `РазницаМесяцев(дата1, дата2)` | Целых месяцев |
| `РазницаЛет(дата1, дата2)` | Целых лет |
| `РазницаЧасов(дата1, дата2)` | Целых часов |
| `РазницаМинут(дата1, дата2)` | Целых минут |
| `РазницаРабочихДней(дата1, дата2)` | Рабочих дней |
| `РазницаРабочихМинут(дата1, дата2)` | Рабочих минут |
| `РазницаРабочихЧасов(дата1, дата2)` | Рабочих часов |
| `РазницаРабочихМинутEx(дата1, дата2)` | Рабочих минут по производственному календарю |
| `РазницаРабочихЧасовEx(дата1, дата2)` | Рабочих часов по производственному календарю |
| `РазницаРабочихДнейEx(дата1, дата2)` | Рабочих дней по производственному календарю |

Функции с суффиксом `Ex` вызывают в базе одноимённые функции `tc_…Ex` — более новую реализацию расчёта по производственному календарю; в панели инструментов редактора они стоят рядом с одноимёнными функциями без суффикса. В группах панели инструментов «Добавить» и «Разница» кнопки расширенных функций подписаны так же, как называются сами функции, — «ДобавитьРабочихМинутEx», «РазницаРабочихДнейEx» и остальные четыре; от базовых операций с датами они отделены разделителем, поэтому в списке видно, где кончаются обычные прибавления дней и часов и начинается расчёт по рабочему графику.

**Округление дат:**

| Функция | Результат |
|---------|----------|
| `УсечьВремя(дата)` | Дата 00:00 |
| `ОкруглитьКонецДня(дата)` | Дата 23:59 |
| `ОкруглитьНачалоНедели(дата)` | Понедельник 00:00 |
| `ОкруглитьКонецНедели(дата)` | Воскресенье 23:59 |
| `ОкруглитьНачалоМесяца(дата)` | 1-е число 00:00 |
| `ОкруглитьКонецМесяца(дата)` | Последний день 23:59 |
| `ОкруглитьНачалоКвартала(дата)` | Начало квартала |
| `ОкруглитьКонецКвартала(дата)` | Конец квартала |
| `ОкруглитьНачалоГода(дата)` | 1 января 00:00 |
| `ОкруглитьКонецГода(дата)` | 31 декабря 23:59 |

**Форматирование:**
```
ОтформатироватьДату(дата, 'dd.MM.yyyy HH:mi')
```

**Часовые зоны:**
```
ПеревестиВЧасовуюЗонуПользователя(дата, ID_пользователя)
```

---

### Логические ДП и выбор пользователей

ДП типа "Галочка" хранит и строковое (`'да'`/`'нет'`), и бинарное значение.

```
-- Через свойство Галочка (бинарное)
ДП.Активен.Галочка = cast(1 as bit)

-- Через текстовое значение  
ДП.Активен = 'да'

-- Проверка наличия значения
ДП.Поле есть значение
ДП.Поле нет значения
ДП.Поле есть ложь
```

ДП «Выбор пользователей» содержит три типа значений: пользователи, группы, орг.единицы.

```
-- Обращение к разным типам
ДП.Согласующие.Пользователи
ДП.Согласующие.Группы
ДП.Согласующие.ОргЕдиницы

-- Проверка: содержит только пользователей (без групп)
(ДП.Согласующие.Группы нет значения)
```

---

### ДП Lookup и Multilookup

Через Lookup можно обращаться к любым атрибутам связанной задачи:

```
-- Атрибуты связанной задачи
ДП.Проект.Название
ДП.Проект.Заказчик.ФИО
ДП.Проект.Срок

-- ДП связанной задачи
ДП.Проект.ДП.Бюджет
```

**Проверка вхождения в список:**
```
ДП.Проект В списке {12345, 67890, 11111}
```

**Обратные ссылки** — если другая категория ссылается на текущую через Lookup, в дереве появляется объект для получения всех ссылающихся задач.

ДП «Выбор нескольких задач из категории» (Multilookup) хранит значение — массив ID задач: `{12345, 67890, 34567}`

```
-- Количество выбранных
Количество(ДП.СвязанныеЗадачи)

-- Проверка наличия задачи в списке
12345 В списке ДП.СвязанныеЗадачи
```

В дереве контекста редактора смарт-выражений Multilookup разворачивается до набора задач целевой категории — доступны их основные атрибуты и ДП (синтаксис обращения к атрибутам единичной задачи совпадает с одиночным Lookup, см. § «ДП Lookup (ссылка на задачу)» выше). Так как Multilookup — массив, обращение к атрибутам задач обычно используется внутри агрегатных функций.

---

### ДП «Таблица» и ДП «Файл»

Структура: строки × колонки. Обращение через ID строки и ID колонки.

```
-- Получение значения ячейки
ДП.Таблица[ID_строки][ID_колонки]

-- Агрегация по колонке
Сумма(ДП.Таблица.Колонки[ID_колонки])
Максимум(ДП.Таблица.Колонки[ID_колонки])
```

**Типы колонок:**

- Текст, Большой текст
- Число
- Дата, ДатаВремя
- Выпадающий список
- Выбор пользователей
- Файл, Мультифайл

Работа с файловыми ДП:

```
-- Для единичного файла (без Мультифайл)
ДП.Документ.Name
ДП.Документ.FileId

-- Для мультифайла (коллекция)
Количество(ДП.Документы)
ДП.Документы[0].Name
```

**Файлы, вложенные в задачу:**
```
-- Все файлы задачи
Связи файлов с задачами

-- Файлы в ДП
Связи файлов с ДП
```

**Версии файла:**
```
-- Ссылка на конкретную версию
FileId.VersionId

-- Например, первая версия
ДП.Документ.FileId + '.' + ВСтроку(1)
```

---

## Работа с коллекциями

Функции агрегации по коллекциям:

| Функция | Описание | Пример |
|---------|----------|--------|
| `Сумма(коллекция)` | Сумма значений | `Сумма(Подзадачи.ДП.Часы)` |
| `Среднее(коллекция)` | Среднее | `Среднее(Задачи.ДП.Оценка)` |
| `Минимум(коллекция)` | Минимальное | `Минимум(Задачи.Срок)` |
| `Максимум(коллекция)` | Максимальное | `Максимум(Задачи.Срок)` |
| `Количество(коллекция)` | Число элементов | `Количество(Исполнители)` |

Преобразует коллекцию в строку:

```
Сконкатенировать(
    EVALXML(выборка_данных),
    'атрибут',
    ','  -- разделитель
)
```

---

## Условная логика и операторы сравнения

Условные конструкции в выражениях:

```
В случае
    если условие1 то результат1
    если условие2 то результат2
    иначе результат_по_умолчанию
конец
```

**Пример:**
```
В случае
    если ДП.Приоритет = 3 то 'Срочно'
    если ДП.Приоритет = 1 то 'Обычно'
    иначе 'Низкий'
конец
```

Доступные операторы сравнения:

| Оператор | Описание |
|----------|----------|
| `=` | Равно |
| `<>` | Не равно |
| `<`, `<=`, `>`, `>=` | Сравнение |
| `В списке {a, b, c}` | Входит в список |
| `Не в списке {a, b, c}` | Не входит в список |
| `И` | Логическое И |
| `ИЛИ` | Логическое ИЛИ |
| `НЕ` | Отрицание |

---

## Примеры типовых запросов

### Примеры фильтрации задач в смарт-выражениях

Отбор задач в нужном статусе:

```
Статус.ID = ID_статуса
```

Отбор задач за период:

```
Дата создания >= НовыеДатаВремя(2024, 1, 1, 0, 0, 0) И
Дата создания <= НовыеДатаВремя(2024, 12, 31, 23, 59, 59)
```

Отбор просроченных задач:

```
Срок < ТекущиеДатаВремя() И
Статус.ID Не в списке {ID_закрытого_статуса}
```

Отбор задач пользователя:

```
ID_пользователя В списке Исполнители.ID
```

Отбор задач без исполнителя:

```
Исполнители нет значения
```

Поиск задач по тексту:

```
СтрокаСодержит(Текст, 'ключевое слово') ИЛИ
СтрокаСодержит(ДП.Описание, 'ключевое слово')
```

Отбор задач с вложениями:

```
Количество(Связи файлов с задачами) > 0
```

Суммирование по связанным задачам:

```
Сумма(
    Отобрать(
        Связанные задачи,
        Статус.ID = ID_закрытого_статуса
    ).ДП.Сумма
)
```

---

## Работа с ресурсами и трудозатратами

Чтение коллекций (исполнители, подписчики, строки таблиц):

| Коллекция | Таблица БД | Содержит |
|-----------|-----------|----------|
| Плановые трудозатраты | `TaskResourcesPlanEntries` | План по дням |
| Плановые ресурсы | `TaskResources` | Общий план по видам |
| Фактические трудозатраты по исполнителям | `TaskResourceFactAggregated` | Факт в разрезе исполнителей |
| Фактические трудозатраты по дням | `TaskResourcesFactEntries` | Факт по дням |

Примеры работы с коллекциями трудозатрат:

```
-- Сумма плановых часов по задаче
Сумма(Плановые ресурсы.Часы)

-- Фактические трудозатраты конкретного исполнителя
Отобрать(
    Фактические трудозатраты по исполнителям,
    Исполнитель.ID = ID_пользователя
)
```

---

## TSQL-режим (прямые SQL-запросы)

Для сложных случаев доступен прямой SQL.

Чтение файлов задачи:

```sql
SELECT 
    CAST(f.FileId AS varchar(MAX)) + '.' + 
    CAST(f.LatestVersionId AS varchar(MAX)) AS FileRef
FROM FileStorageFiles f
JOIN FileStorageFileToTaskLinks l ON f.FileId = l.FileId
WHERE l.IsDeleted = 0 
  AND l.TaskId = @TaskId
```

Чтение файлов из файловых ДП:

```sql
SELECT 
    CAST(f.FileId AS varchar(MAX)) + '.' + 
    CAST(f.LatestVersionId AS varchar(MAX)) AS FileRef
FROM FileStorageFiles f
JOIN FileStorageFileToExtParamLinks l ON f.FileId = l.FileId
WHERE l.TaskId = @TaskId 
  AND l.ExtParamId = @ExtParamId
```

Таблицы файлового хранилища:

| Таблица | Описание |
|---------|----------|
| `FileStorageFiles` | Метаданные файлов |
| `FileStorageFileVersions` | Версии файлов |
| `FileStorageFileToTaskLinks` | Связи файлов с задачами |
| `FileStorageFileToExtParamLinks` | Связи файлов с ДП |
| `FileProviders` | Провайдеры хранилища |

---

## Типы возвращаемых значений и применение

Смарт-выражения возвращают значения перечисленных ниже типов и применяются для фильтрации, вычислений, валидации и отчётности.

| Тип | Описание |
|-----|----------|
| `String` | Строка |
| `Integer` | Целое число |
| `Decimal` | Десятичная дробь |
| `Boolean` | Логическое (true/false) |
| `DateTime` | Дата и время |
| `Entity.User` | Пользователь |
| `Entity.Task` | Задача |
| `Entity.File` | Файл |
| `Collection<T>` | Коллекция объектов |

Смарт-выражения применяются для:

1. **Фильтрации задач** — получение списка по условиям
2. **Вычисляемых полей** — расчёт значений на лету
3. **Валидации** — проверка условий перед действиями
4. **Отчётности** — агрегация данных по коллекциям

Рекомендации по составлению выражений:

- Используйте `IsNullString()` для защиты от null
- Проверяйте типы при конвертации
- Для сложных запросов предпочитайте TSQL
- Тестируйте выражения на реальных данных через интерфейс
