Решение задач 1С:Предприятие 8 на примерах

Таблица значений в 1С: создание, поиск строк, передача в запрос и на форму

АлгоритмыОпубликовано

Таблица значений нужна почти в любой обработке данных на 1С. Здесь собраны готовые фрагменты кода для всех частых операций: от создания колонок с типами до передачи таблицы в запрос и работы с ней на управляемой форме, а также ошибки, которые встречаются чаще всего.

Задача: собрать и обработать данные в памяти

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

Ниже — полный цикл работы с таблицей значений программно: создание с типизированными колонками, добавление строк, поиск, сортировка, свёртка, удаление строк в цикле, преобразования «массив ↔ таблица», передача в запрос и вывод на управляемую форму.

Создание таблицы значений и колонки с типами

Таблица создаётся конструктором Новый ТаблицаЗначений, колонки добавляются через коллекцию Колонки. Второй параметр метода Колонки.Добавить() — ОписаниеТипов. Его можно не указывать, но тогда колонка примет значения любого типа, а это создаёт проблемы при передаче таблицы в запрос и при выводе на форму.

&НаСервере
Функция НоваяТаблицаТоваров()

    Таблица = Новый ТаблицаЗначений;

    Таблица.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
    Таблица.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));
    Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
    Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
    Таблица.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
    Таблица.Колонки.Добавить("ДатаОтгрузки", Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата)));

    Возврат Таблица;

КонецФункции

Порядок параметров конструктора ОписаниеТипов: типы, квалификаторы числа, квалификаторы строки, квалификаторы даты. Поэтому для строки и даты пропущенные параметры отмечаются запятыми. Если колонке нужны несколько типов, перечислите их через запятую в одной строке: Новый ОписаниеТипов("СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица").

Добавить колонку в таблицу значений можно и после заполнения: новая колонка появится у всех существующих строк со значением по умолчанию для своего типа. Колонку вставляют на нужную позицию методом Колонки.Вставить(Индекс, Имя, Тип), удаляют — Колонки.Удалить(), проверяют наличие — Колонки.Найти("Имя"), который вернёт Неопределено, если колонки нет.

Как добавить строку в таблицу значений

Метод Добавить() создаёт пустую строку в конце таблицы и возвращает её — дальше значения присваиваются по именам колонок. Если данные уже лежат в структуре или в строке другой коллекции с теми же именами, удобнее ЗаполнитьЗначенияСвойств().

Таблица = НоваяТаблицаТоваров();

НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00-0001");
НоваяСтрока.Склад = Справочники.Склады.Основной;
НоваяСтрока.Количество = 10;
НоваяСтрока.Сумма = 1500;

// Заполнение из структуры с совпадающими именами
Данные = Новый Структура("Номенклатура, Количество, Сумма", Товар, 2, 300);
ЗаполнитьЗначенияСвойств(Таблица.Добавить(), Данные);

// Вставка строки на конкретную позицию
ПерваяСтрока = Таблица.Вставить(0);

Обход строк и доступ к значениям

Таблицу перебирают циклом Для Каждого СтрокаТаблицы Из Таблица Цикл. К значению колонки обращаются через точку (СтрокаТаблицы.Сумма) или по имени в квадратных скобках (СтрокаТаблицы["Сумма"]) — второй вариант нужен, когда имя колонки хранится в переменной. Строку можно получить и по индексу: Таблица[0] — первая строка, Таблица.Количество() — их число, а Таблица.Индекс(Строка) возвращает позицию строки.

Для Каждого СтрокаТаблицы Из Таблица Цикл
    СтрокаТаблицы.Сумма = Окр(СтрокаТаблицы.Сумма, 2);
    Для Каждого Колонка Из Таблица.Колонки Цикл
        Значение = СтрокаТаблицы[Колонка.Имя];
    КонецЦикла;
КонецЦикла;

Изменять значения в строках внутри такого цикла безопасно — проблемы начинаются только при удалении строк, о чём ниже. Для быстрого пересчёта колонки целиком удобна пара ВыгрузитьКолонку() и ЗагрузитьКолонку(): выгрузили массив, обработали, загрузили обратно в ту же или другую колонку.

Поиск в таблице значений: Найти и НайтиСтроки

Для поиска есть два метода с разным назначением.

Найти(Значение, Колонки) ищет одно значение и возвращает первую подходящую строку или Неопределено. Второй параметр — имена колонок через запятую, по которым идёт поиск; если его не указать, значение ищется во всех колонках.

НайтиСтроки(Отбор) принимает структуру с условиями «колонка — значение», все условия объединяются по «И». Возвращается массив строк — пустой, если ничего не найдено.

// Одна строка по значению
Строка = Таблица.Найти(Товар, "Номенклатура");
Если Строка <> Неопределено Тогда
    Строка.Количество = Строка.Количество + 1;
КонецЕсли;

// Все строки по нескольким условиям
Отбор = Новый Структура("Номенклатура, Склад", Товар, Справочники.Склады.Основной);
НайденныеСтроки = Таблица.НайтиСтроки(Отбор);
Для Каждого СтрокаТаблицы Из НайденныеСтроки Цикл
    СтрокаТаблицы.Комментарий = "Проверено";
КонецЦикла;

При частом поиске по одной и той же колонке создайте индекс: Таблица.Индексы.Добавить("Номенклатура, Склад"). На таблицах в десятки тысяч строк НайтиСтроки() с индексом работает на порядки быстрее перебора циклом.

Сортировка, свёртка, итоги и копирование

// Сортировка по нескольким колонкам
Таблица.Сортировать("Склад, Сумма Убыв");

// Группировка: колонки группировки и суммируемые колонки
Таблица.Свернуть("Номенклатура, Склад", "Количество, Сумма");

// Итог по числовой колонке
ВсегоСумма = Таблица.Итог("Сумма");

// Копия только части строк и колонок
ОтборСклада = Новый Структура("Склад", Справочники.Склады.Основной);
ТаблицаСклада = Таблица.Скопировать(ОтборСклада, "Номенклатура, Количество");

// Уникальные значения колонки
Уникальные = Таблица.Скопировать(, "Номенклатура");
Уникальные.Свернуть("Номенклатура");
МассивТоваров = Уникальные.ВыгрузитьКолонку("Номенклатура");

Свернуть() удаляет колонки, не перечисленные ни в одном из параметров, — если они понадобятся, сверните копию. Скопировать() принимает первым параметром либо структуру отбора, либо массив строк, а без параметров создаёт полную копию с той же структурой. Пустую таблицу с теми же колонками даёт СкопироватьКолонки().

Удаление строк в цикле

Классическая ошибка — удалять строки внутри Для Каждого по самой таблице: после удаления индексы сдвигаются, и часть строк пропускается. Есть два надёжных способа.

// Способ 1: обратный обход по индексу
Индекс = Таблица.Количество() - 1;
Пока Индекс >= 0 Цикл
    Если Таблица[Индекс].Количество = 0 Тогда
        Таблица.Удалить(Индекс);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

// Способ 2: сначала найти, потом удалить
СтрокиКУдалению = Таблица.НайтиСтроки(Новый Структура("Количество", 0));
Для Каждого СтрокаКУдалению Из СтрокиКУдалению Цикл
    Таблица.Удалить(СтрокаКУдалению);
КонецЦикла;

Во втором случае цикл идёт по отдельному массиву, поэтому удаление из таблицы его не ломает. Полностью очищает таблицу метод Очистить() — колонки при этом сохраняются.

Массив в таблицу значений и обратно

Простой массив значений загружается в колонку методом ЗагрузитьКолонку(), но строк в таблице должно быть не меньше, чем элементов. Поэтому сначала добавляют нужное количество строк. Массив структур удобнее переносить через ЗаполнитьЗначенияСвойств().

// Массив значений -> колонка таблицы
Функция МассивВТаблицуЗначений(Массив, ИмяКолонки, ОписаниеТипа) Экспорт
    Таблица = Новый ТаблицаЗначений;
    Таблица.Колонки.Добавить(ИмяКолонки, ОписаниеТипа);
    Для Каждого Элемент Из Массив Цикл
        НоваяСтрока = Таблица.Добавить();
        НоваяСтрока[ИмяКолонки] = Элемент;
    КонецЦикла;
    Возврат Таблица;
КонецФункции

// Массив структур -> таблица значений (колонки по ключам первой структуры)
Функция МассивСтруктурВТаблицуЗначений(МассивСтруктур) Экспорт
    Таблица = Новый ТаблицаЗначений;
    Если МассивСтруктур.Количество() = 0 Тогда
        Возврат Таблица;
    КонецЕсли;
    Для Каждого КлючИЗначение Из МассивСтруктур[0] Цикл
        Таблица.Колонки.Добавить(КлючИЗначение.Ключ);
    КонецЦикла;
    Для Каждого ЭлементМассива Из МассивСтруктур Цикл
        ЗаполнитьЗначенияСвойств(Таблица.Добавить(), ЭлементМассива);
    КонецЦикла;
    Возврат Таблица;
КонецФункции

// Таблица значений -> массив структур
Функция ТаблицаВМассивСтруктур(Таблица) Экспорт
    ИменаКолонок = "";
    Для Каждого Колонка Из Таблица.Колонки Цикл
        ИменаКолонок = ИменаКолонок + ?(ИменаКолонок = "", "", ",") + Колонка.Имя;
    КонецЦикла;
    Результат = Новый Массив;
    Для Каждого СтрокаТаблицы Из Таблица Цикл
        Элемент = Новый Структура(ИменаКолонок);
        ЗаполнитьЗначенияСвойств(Элемент, СтрокаТаблицы);
        Результат.Добавить(Элемент);
    КонецЦикла;
    Возврат Результат;
КонецФункции

В конфигурациях на БСП для преобразования таблицы в массив структур есть готовая функция ОбщегоНазначения.ТаблицаЗначенийВМассив(). Массив структур удобен, когда данные нужно передать на клиент: сама таблица значений в тонком клиенте недоступна.

Обратите внимание: функция МассивСтруктурВТаблицуЗначений создаёт колонки без типов. Для дальнейшей передачи такой таблицы в запрос добавьте описания типов явно.

Таблица значений в запрос

Таблицу можно передать параметром и выбрать из неё данные, но только в запросе, который помещает результат во временную таблицу. Соединять параметр-таблицу с другими таблицами в том же запросе нельзя — сначала ПОМЕСТИТЬ, потом соединение в следующем запросе пакета.

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Т.Номенклатура КАК Номенклатура,
|    Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
|    &ТаблицаТоваров КАК Т
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    ВТ_Товары.Номенклатура КАК Номенклатура,
|    ВТ_Товары.Количество КАК Количество,
|    ВТ_Товары.Номенклатура.Артикул КАК Артикул
|ИЗ
|    ВТ_Товары КАК ВТ_Товары";

Запрос.УстановитьПараметр("ТаблицаТоваров", Таблица);
Результат = Запрос.Выполнить().Выгрузить();

Главное требование — у колонок, которые участвуют в запросе, должны быть заданы типы. Колонка без ОписаниеТипов вызывает ошибку «Тип не может быть выбран в запросе». Если таблица пришла из чужого кода без типов, создайте новую с типизированными колонками и перенесите данные через ЗаполнитьЗначенияСвойств().

Таблица значений на управляемой форме

На управляемой форме нельзя хранить настоящую ТаблицаЗначений: реквизит формы с этим типом на клиенте представлен коллекцией ДанныеФормыКоллекция. С ней работают так же — Добавить(), НайтиСтроки(), Удалить(), — но методы Свернуть(), Итог() и Скопировать() есть только у исходной таблицы. Поэтому для сложной обработки данные переводят в таблицу значений на сервере и обратно.

&НаСервере
Процедура СвернутьТоварыНаСервере()

    // Реквизит формы "Товары" с типом ТаблицаЗначений
    ТаблицаТоваров = РеквизитФормыВЗначение("Товары");
    ТаблицаТоваров.Свернуть("Номенклатура", "Количество, Сумма");
    ЗначениеВРеквизитФормы(ТаблицаТоваров, "Товары");

    // Альтернатива для коллекции данных формы
    // ТаблицаТоваров = Товары.Выгрузить();
    // Товары.Загрузить(ТаблицаТоваров);

КонецПроцедуры

Колонки реквизита-таблицы задаются в конфигураторе как подчинённые реквизиты формы. Если колонки неизвестны заранее, их добавляют программно методом формы ИзменитьРеквизиты(), а затем создают элементы через Элементы.Добавить() — это отдельная тема, но важно помнить: без реквизита-колонки данные в нужное поле не попадут.

Частые ошибки

  • Колонки без типов — проблемы в запросе, при выводе на форму и при сравнении чисел, пришедших строкой.
  • Удаление строк в прямом цикле Для Каждого по самой таблице — часть строк остаётся.
  • Перебор циклом вместо НайтиСтроки() с индексом на больших объёмах.
  • Вызов Свернуть() на исходной таблице, когда остальные колонки ещё нужны.
  • Попытка передать таблицу значений на клиент — используйте реквизит формы или массив структур.

Шпаргалка по методам

ЗадачаМетодРезультат
Добавить строкуДобавить(), Вставить(Индекс)Новая строка
Найти одну строкуНайти(Значение, "Колонки")Строка или Неопределено
Найти все строкиНайтиСтроки(Структура)Массив строк
СгруппироватьСвернуть(Группировки, Суммируемые)Изменённая таблица
Сумма колонкиИтог("Колонка")Число
Колонка в массивВыгрузитьКолонку("Колонка")Массив
Часть таблицыСкопировать(Отбор, "Колонки")Новая таблица

Актуальность для новых версий

Объект ТаблицаЗначений и перечисленные методы работают во всех версиях платформы 8.3, включая 8.3.2x. Таблица значений доступна только на сервере и в толстом клиенте; в тонком и веб-клиенте с данными работают через реквизит формы (ДанныеФормыКоллекция). Функция ОбщегоНазначения.ТаблицаЗначенийВМассив() есть только в конфигурациях на БСП — в остальных используйте собственную функцию из примера.

Частые вопросы

Как добавить колонку в таблицу значений с типом?

Вызовите Колонки.Добавить("Имя", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2))). Для строки и даты квалификаторы передаются третьим и четвёртым параметрами ОписаниеТипов.

Чем Найти() отличается от НайтиСтроки()?

Найти() ищет одно значение и возвращает первую подходящую строку или Неопределено. НайтиСтроки() принимает структуру с несколькими условиями и возвращает массив всех подходящих строк.

Почему таблица значений не передаётся в запрос?

У колонок, используемых в запросе, должны быть заданы типы, а выбирать из параметра-таблицы можно только в запросе с ПОМЕСТИТЬ. Соединения делают уже с полученной временной таблицей.

Как вывести таблицу значений на управляемую форму?

Создайте реквизит формы типа ТаблицаЗначений с нужными колонками и на сервере вызовите ЗначениеВРеквизитФормы(Таблица, "ИмяРеквизита"). Обратно данные получают методом РеквизитФормыВЗначение().

Расширение конфигурации 1С: создание, установка и перехват методов

Алгоритмы

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

Запись в регистр сведений 1С программно: менеджер записи, набор записей и удаление

Алгоритмы

Разбор записи в регистр сведений из кода 1С: когда брать менеджер записи, а когда набор с отбором, как удалить запись или очистить регистр и почему записи становятся неуникальными.

Хеширование HMAC-SHA1 и HMAC-SHA256 средствами 1С

Алгоритмы

Готовая функция на языке 1С, которая считает HMAC-SHA1 и HMAC-SHA256 и возвращает результат в Hex и Base64. Пригодится для подписи запросов к сторонним API без внешних компонент.

XML в 1С программно: запись, чтение, DOM и выгрузка данных

Интеграция и веб-сервисы

Готовые примеры работы с XML на встроенном языке 1С: запись и чтение, DOM с XPath, преобразование XML в структуру, выгрузка справочника и разбор ошибок парсинга.