Почему при переборе Таблицы Значений объекты превращаются в строки и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Анализ исходной проблемы: итерация по строке вместо таблицы

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


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

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

// Ошибочный цикл
Для каждого ТекущаяСтрока из СтрокаТаблицаДействий Цикл 
    СтрТЧ = Объект.ЗаполнениеРеквизитовДоговора.Добавить();
    СтрТЧ.РеквизитДоговора = ТекущаяСтрока.Наименование; // Ошибка!
КонецЦикла;

В чем здесь заключается главная ошибка? Обратите внимание на объект, который обходится циклом Для каждого. Автор использует переменную СтрокаТаблицаДействий. В контексте предыдущего цикла Пока эта переменная хранит последнюю добавленную строку таблицы (объект типа СтрокаТаблицыЗначений), а не всю таблицу целиком.

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

Правильный обход Таблицы Значений

Для корректного получения данных необходимо обходить саму коллекцию — объект ТаблицаЗначений. В этом случае на каждой итерации переменная цикла будет содержать полноценный объект СтрокаТаблицыЗначений, у которого есть доступ к колонкам по их именам. Посмотрим на исправленный вариант:


// Правильный цикл по всей таблице
Для каждого ТекущаяСтрока Из ТаблицаДействий Цикл
    СтрТЧ = Объект.ЗаполнениеРеквизитовДоговора.Добавить();
    
    // Теперь ТекущаяСтрока — это строка таблицы, 
    // и мы можем обращаться к её колонкам:
    СтрТЧ.РеквизитДоговора = ТекущаяСтрока.Наименование;
    СтрТЧ.Комментарий = ТекущаяСтрока.ИсточникДляЗаполнения;
КонецЦикла;

В данном случае, если в колонку "Наименование" была записана ссылка (например, СправочникСсылка.Договоры), она останется ссылкой. Важно понимать: 1С никогда не меняет тип данных самостоятельно при обычном переборе. Если вам кажется, что "объект превратился в строку", скорее всего, вы либо обращаетесь не к тому объекту, либо смотрите на представление ссылки в отладчике, которое действительно выглядит как текстовое наименование.

Типизация колонок: зачем это нужно?

В примере выше колонки добавлялись простым способом: ТаблицаДействий.Колонки.Добавить("Наименование"). При таком объявлении колонка имеет тип Произвольный. Это означает, что в одну и ту же ячейку в разных строках можно записать и число, и ссылку, и строку. Хотя платформа это позволяет, для стабильной работы (особенно при взаимодействии с элементами интерфейса) рекомендуется использовать явную типизацию.

Рассмотрим, как правильно задать тип колонки:


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

ОписаниеТиповСсылки = Новый ОписаниеТипов(МассивТипов);

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

Использование ОписаниеТипов дает несколько преимуществ:

  1. Контроль данных: при попытке записать в колонку неподходящий тип (например, случайную строку вместо ссылки), система попытается привести значение к нужному типу или запишет пустую ссылку, что предотвратит ошибки в дальнейшем коде.
  2. Интерфейс: если такая таблица будет выведена на форму, система автоматически создаст правильный элемент управления (поле выбора с кнопкой "открыть" или "выбрать").
  3. Производительность: типизированные структуры обрабатываются платформой чуть эффективнее.

Передача Таблицы Значений между Клиентом и Сервером

Автор темы упоминал, что планировал передать таблицу на клиент. Здесь кроется еще одна важная особенность платформы 1С 8.3. Объект ТаблицаЗначений является исключительно серверным. Его нельзя передать в процедуру, выполняемую &НаКлиенте.

Проанализируем способы решения этой задачи:

1. Преобразование в массив структур

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


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

2. Использование ДанныхФормыКоллекция

Если таблица должна отображаться на форме и редактироваться пользователем, ее нужно создать как Реквизит формы с типом "Таблица значений". На уровне формы этот объект превращается в ДанныеФормыКоллекция, который доступен и на клиенте, и на сервере.

Для преобразования программно созданной серверной ТаблицыЗначений в реквизит формы используются специальные методы:

Особенности работы с Табличными Частями объекта

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

Если в ТЧ реквизит "Договор" имеет тип СправочникСсылка.Договоры, а вы в коде по ошибке передаете туда строку (например, просто текст с названием договора), 1С не сможет сопоставить этот текст с реальной ссылкой в базе данных. В результате поле останется пустым или вызовется ошибка "Несоответствие типов".

Рассмотрим правильный алгоритм переноса:

  1. Убедитесь, что в исходной ТаблицеЗначений в колонке лежит именно Ссылка (объект базы), а не Наименование (строка).
  2. Используйте метод ЗаполнитьЗначенияСвойств(СтрокаПриемник, СтрокаИсточник) для быстрого копирования данных между строкой ТЗ и строкой ТЧ, если имена колонок совпадают.

Для каждого СтрокаТЗ Из ТаблицаДействий Цикл
    НоваяСтрокаТЧ = Объект.ЗаполнениеРеквизитовДоговора.Добавить();
    // Копирует все совпадающие по именам колонки автоматически
    ЗаполнитьЗначенияСвойств(НоваяСтрокаТЧ, СтрокаТЗ); 
КонецЦикла;

Подведение итогов

Подводя итог, можно сказать: ТаблицаЗначений — это мощный инструмент, требующий внимательности к двум вещам: контексту исполнения (сервер) и правильности обхода коллекции. Ошибка с "превращением в строку" почти всегда связана с неправильным использованием цикла Для каждого или обращением к представлению объекта вместо самого объекта. Всегда проверяйте, что именно вы перебираете, и старайтесь типизировать колонки еще на этапе их создания.

← На главную