Работа с таблицей значений в 1С 8.3: от создания до вывода на печать

· 9 мин

Содержание

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

Частая путаница: таблицу значений смешивают с табличным документом. Это разные вещи. Таблица значений (ТаблицаЗначений) — структура данных для обработки: строки и колонки, с которыми работает код. Табличный документ (ТабличныйДокумент) — то, что видно на экране и уходит на печать или в Excel. Ниже — как создать таблицу значений, заполнить её разными способами, перенести данные между таблицами и в итоге вывести на печать.

Таблица значений и табличный документ: что из них для чего

Коротко по назначению:

  • 🗃️ Таблица значений — временное хранилище в памяти. Колонки произвольных типов, любое число строк. Данные пропадают, как только переменная выходит из области видимости. Применяют для расчётов, промежуточных выборок, передачи наборов строк между процедурами.
  • 📄 Табличный документ — визуальный объект. Строится из областей макета, умеет выводиться на экран, печататься и сохраняться в .xlsx/.mxl. Данные для него обычно берут как раз из таблицы значений.
  • 🧾 Табличная часть документа (например, Товары в накладной) — реквизит объекта, который хранится в базе. Её выгружают в таблицу значений и загружают обратно.

Правило простое: считаете и обрабатываете — таблица значений; показываете и печатаете — табличный документ; храните в документе — табличная часть.

Создаётся таблица значений одной строкой:

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

Пустая таблица без колонок бесполезна, поэтому сразу описывают колонки — об этом дальше.

Создание и заполнение таблицы значений вручную

Сначала добавляют колонки, затем строки. У колонки можно задать тип через ОписаниеТипов — тогда 1С будет контролировать, что кладут в ячейку.

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

Первая колонка без типа — в неё можно положить значение любого типа. Для числовых колонок квалификатор задаёт разрядность и число знаков после запятой.

Строки добавляют методом Добавить(). Он возвращает новую строку, у которой поля заполняют по имени колонки:

НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = "Бумага А4";
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 320;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;

Если строку нужно вставить не в конец, а в определённое место, есть Вставить(Индекс) — он принимает номер позиции (счёт с нуля) и так же возвращает строку.

Заполнить таблицу в цикле по другому набору данных — типовой приём. Например, перенесём строки из массива структур:

Для Каждого Элемент Из МассивТоваров Цикл
    Стр = ТЗ.Добавить();
    Стр.Номенклатура = Элемент.Наименование;
    Стр.Количество   = Элемент.Кол;
    Стр.Цена         = Элемент.Цена;
    Стр.Сумма        = Элемент.Кол * Элемент.Цена;
КонецЦикла;

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

МассивЦен = Новый Массив;
МассивЦен.Добавить(320);
МассивЦен.Добавить(55);
ТЗ.ЗагрузитьКолонку(МассивЦен, "Цена");

Обратная операция — ВыгрузитьКолонку("Цена") — вернёт массив значений одной колонки. Это выручает, когда нужно передать список в параметр запроса или в В (&Массив).

Итог по числовой колонке считает метод Итог():

ОбщаяСумма = ТЗ.Итог("Сумма");

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

Если ТЗ.Количество() > 0 Тогда
    ТЗ.Удалить(0);     // по индексу
КонецЕсли;

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

Самый частый способ наполнить таблицу реальными данными — выгрузить результат запроса. Метод Выгрузить() у результата запроса сам создаёт таблицу значений с нужными колонками:

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |   Остатки.Номенклатура КАК Номенклатура,
    |   Остатки.КоличествоОстаток КАК Количество
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки";

РезультатЗапроса = Запрос.Выполнить();
ТЗ = РезультатЗапроса.Выгрузить();

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

Если запрос ограничивают условием, значение передают через параметр, а не склеивают в текст запроса — так безопаснее и работает контроль типов:

Запрос.Текст =
    "ВЫБРАТЬ Остатки.Номенклатура КАК Номенклатура,
    |   Остатки.КоличествоОстаток КАК Количество
    |ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&МоментВремени,) КАК Остатки
    |ГДЕ Остатки.КоличествоОстаток > 0";
Запрос.УстановитьПараметр("МоментВремени", ТекущаяДата());
ТЗ = Запрос.Выполнить().Выгрузить();

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

Если результат содержит группировки (ИТОГИ ... ПО), выгрузку делают по иерархии через ОбходРезультатаЗапроса. Для плоской таблицы хватает ОбходРезультатаЗапроса.Прямой, который и используется по умолчанию.

Запрос с обращением к базе выполняют на сервере. Если код в клиентском контексте управляемой формы — выносите его в серверную процедуру (&НаСервере или &НаСервереБезКонтекста), иначе получите ошибку обращения к менеджеру запросов.

После выгрузки с таблицей работают как с обычной: дополняют колонки, пересчитывают, сортируют:

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

Строку сортировки пишут как «Колонка Возр» или «Колонка Убыв», несколько колонок — через запятую.

Копирование, свёртка и перенос строк между таблицами

Чтобы не портить исходные данные, таблицу копируют методом Скопировать(). Без параметров он создаёт полную независимую копию:

КопияТЗ = ТЗ.Скопировать();

У Скопировать() есть и выборочная форма. Первым параметром передают массив строк или отбор, вторым — список нужных колонок через запятую:

// только колонки Номенклатура и Сумма
УрезаннаяТЗ = ТЗ.Скопировать(, "Номенклатура, Сумма");

// только строки, найденные по отбору
ОтобранныеСтроки = ТЗ.НайтиСтроки(Новый Структура("Дефицит", Истина));
ТолькоДефицит = ТЗ.Скопировать(ОтобранныеСтроки);

НайтиСтроки() принимает структуру «колонка = значение» и возвращает массив подходящих строк. Для поиска одной строки есть Найти(Значение, "Колонка").

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

ТЗ.Свернуть("Номенклатура", "Количество, Сумма");

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

Перенести строки из одной таблицы в другую с совпадающими колонками можно простым циклом с ЗаполнитьЗначенияСвойств:

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

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

Загрузка данных в табличную часть документа и на форму

Табличная часть документа (Товары, Услуги и подобные) — это не таблица значений, а реквизит объекта. Но между ними есть прямой мост: методы Загрузить() и Выгрузить().

Выгрузка табличной части в таблицу значений:

ТЗ = Документ.Товары.Выгрузить();

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

Документ.Товары.Загрузить(ТЗ);

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

С управляемой формой свой нюанс. Реквизит формы с типом «таблица значений» на клиенте представлен объектом ДанныеФормыКоллекция, а не ТаблицаЗначений. Чтобы на сервере поработать с ним как с обычной таблицей значений, преобразуют в обе стороны:

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

    // ... обработка строк ТЗ ...

    // обратно в реквизит формы
    ЗначениеВРеквизитФорму(ТЗ, "ТоварыНаФорме");
КонецПроцедуры

РеквизитФормыВЗначение и ЗначениеВРеквизитФорму работают только в серверном контексте. Если же вы в модуле самого документа, преобразование не нужно — табличная часть доступна напрямую через Документ.Товары.Выгрузить() и Загрузить().

Вывод таблицы в печатную форму и выгрузка в Excel

Напрямую таблицу значений на печать не отправить — её сначала переносят в табличный документ. Классический способ: макет с именованными областями.

  1. Создайте макет типа «Табличный документ» в объекте (обработке, документе). Добавьте области: «Шапка», «Строка», «Подвал». В области «Строка» разместите ячейки с параметрами — имена параметров задают в свойствах ячейки.
  2. В коде получите макет и соберите табличный документ, выводя область «Строка» для каждой строки таблицы значений:
Макет  = ПолучитьМакет("МакетПечати");
ТабДок = Новый ТабличныйДокумент;

ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ТабДок.Вывести(ОбластьШапка);

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

ТабДок.Показать();

Вывести() дописывает область вниз документа, Показать() открывает готовый табличный документ на экране. Из открытого документа пользователь сам сохранит его в Excel через Файл → Сохранить как.

Итоговую строку выводят так же — отдельной областью «Подвал», в параметр которой кладут посчитанный заранее итог:

ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
ОбластьПодвал.Параметры.ИтогоСумма = ТЗ.Итог("Сумма");
ТабДок.Вывести(ОбластьПодвал);

Перед показом табличному документу можно запретить редактирование и настроить вид: ТабДок.ТолькоПросмотр = Истина, ТабДок.ОтображатьСетку = Ложь. Это косметика, но для печатной формы она уместна — пользователь не испортит макет случайным вводом.

Чтобы отдать файл программно, пригодится Записать() с типом формата:

ТабДок.Записать("C:\Выгрузка\Остатки.xlsx",
    ТипФайлаТабличногоДокумента.XLSX);

Запись в файл по пути работает только на той машине, где выполняется код. На сервере и в веб-клиенте путь к диску пользователя недоступен — там файл помещают во временное хранилище и отдают через ПолучитьФайл().

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

Частые ошибки при работе с таблицей значений

Ошибки здесь однотипные и легко узнаваемые:

  • ❌ «Поле объекта не обнаружено» при загрузке в табличную часть — не совпали имена колонок и реквизитов. Проверьте написание: Кол в таблице и Количество в документе не склеятся сами.
  • ❌ Пустая таблица после выгрузки запроса — у полей запроса не задали псевдонимы через КАК, либо запрос реально ничего не вернул. Проверьте текст в консоли запросов.
  • ❌ Ошибка контекста при Запрос.Выполнить() на клиенте — запрос к базе выполняется только на сервере. Вынесите вызов в серверную процедуру.
  • ❌ Сбитые итоги после Свернуть() — в суммируемые колонки попала колонка со строками или ссылками. Суммировать можно только числовые колонки.
  • ❌ Изменение таблицы прямо в цикле Для Каждого — удаление строки во время обхода сбивает перебор. Удаляйте обратным циклом по индексу: Для Сч = ТЗ.Количество() - 1 По 0 Цикл.
Проверьте себя
Каким методом результат запроса превращают в таблицу значений?
Что хранит данные в базе, а не только в памяти?
Чем объединить одинаковые строки с суммированием чисел?

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

Как очистить таблицу значений, не пересоздавая её?
Метод ТЗ.Очистить() удаляет все строки, но сохраняет колонки. Если нужно убрать и колонки — ТЗ.Колонки.Очистить().
Можно ли задать колонке тип ссылки на справочник?
Да. В ОписаниеТипов передают тип ссылки, например Новый ОписаниеТипов("СправочникСсылка.Номенклатура"). Тогда в ячейку нельзя будет положить значение другого типа.
Как перенести таблицу значений с сервера на клиент управляемой формы?
Напрямую нельзя — тип неподходящий для передачи. Либо кладут таблицу в реквизит формы через ЗначениеВРеквизитФорму, либо помещают во временное хранилище ПоместитьВоВременноеХранилище и забирают по адресу.
Чем отличается `Добавить()` от `Вставить()`?
Добавить() всегда дописывает строку в конец. Вставить(Индекс) помещает новую строку на указанную позицию, сдвигая остальные вниз. Обе возвращают созданную строку.
Как найти строку по значению колонки?
Для одной строки — ТЗ.Найти(Значение, "Колонка"), вернёт строку или Неопределено. Для нескольких — ТЗ.НайтиСтроки(Новый Структура("Колонка", Значение)), вернёт массив строк.