При разработке на платформе 1С:Предприятие 8 (особенно на управляемых формах) программисты часто сталкиваются с ситуацией, когда при передаче данных из таблицы на форме в программный код для обработки в запросе типы данных «слетают». Самая распространенная проблема — превращение ссылки на справочник в обычную строку. Это делает невозможным использование данных во временных таблицах запроса, так как типизация колонок не совпадает с ожидаемой структурой метаданных.
Рассмотрим детально, почему это происходит, и разберем правильные подходы к решению этой задачи.
Проанализируем ситуацию, описанную в вопросе. Программист пытается заранее подготовить структуру таблицы значений (ТЗ), создавая колонки с четко заданным описанием типов:
ВТ = Новый ТаблицаЗначений;
НоменклатураТип = Новый ОписаниеТипов("СправочникСсылка.Номенклатура");
ВТ.Колонки.Добавить("Наименование", НоменклатураТип, "Наименование (у поставщика)");
На первый взгляд, все верно. Однако далее следует строка кода, которая полностью перечеркивает предыдущие действия:
ВТ = ВыборВЗаказ.Выгрузить(,"Наименование,Цена,Выбран");
Разберем, что здесь происходит с точки зрения платформы. Метод Выгрузить() для объекта ДанныеФормыКоллекция (которым является таблица на форме) не заполняет существующий объект, переданный в переменную, а создает новый экземпляр типа ТаблицаЗначений. В момент выполнения присваивания ВТ = ... ссылка на ту таблицу, которую мы так тщательно типизировали, теряется. Вместо нее в переменную ВТ записывается новая таблица, структура которой полностью определяется метаданными реквизита формы ВыборВЗаказ.
Если в реквизитах формы колонка "Наименование" имела тип Строка, то и в выгруженной таблице она будет строкой. Именно поэтому предварительное создание колонок в данном контексте бесполезно.
Прежде чем приступать к программным манипуляциям, необходимо убедиться, что данные на самой форме имеют корректный тип. Выясним причину: часто при «быстрой» разработке или доработке чужого кода колонки табличных частей или динамических списков на форме создаются как текстовые поля.
Важный момент: Если в колонке на форме отображается название номенклатуры, это еще не значит, что там хранится ссылка. Проверьте свойства реквизита формы (в дереве реквизитов, а не в элементах формы). Если там указана Строка, то метод Выгрузить() всегда будет возвращать строку.
Чтобы исправить это на уровне интерфейса:
СправочникСсылка.Номенклатура вместо Строка.Если типы в форме настроены верно, мы можем упростить код и избежать лишних действий по созданию промежуточных объектов. Рассмотрим пример из обсуждения, который является наиболее лаконичным и правильным:
Запрос = Новый Запрос;
// Выгружаем только нужные колонки сразу в параметр запроса
Запрос.УстановитьПараметр("Таблица", ВыборВЗаказ.Выгрузить(, "Наименование, Цена, Выбран"));
Запрос.Текст =
"ВЫБРАТЬ
| Таблица.Наименование КАК Наименование,
| Таблица.Цена КАК Цена,
| Таблица.Выбран КАК Выбран
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &Таблица КАК Таблица
|;
|// Дальнейшая работа с временной таблицей";
При таком подходе платформа автоматически создаст временную таблицу с типами, которые соответствуют типам колонок в ДанныеФормыКоллекция.
Иногда возникают ситуации, когда колонка должна оставаться строкой (например, если туда загружаются данные из внешнего файла, которые еще не сопоставлены со справочником). В этом случае нам нужно вручную заполнить типизированную таблицу значений.
Проанализируем алгоритм «ручного» переноса с приведением типов:
// 1. Создаем таблицу с жесткой типизацией
ВТ = Новый ТаблицаЗначений;
НоменклатураТип = Новый ОписаниеТипов("СправочникСсылка.Номенклатура");
ВТ.Колонки.Добавить("Номенклатура", НоменклатураТип);
ВТ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
// 2. Обходим данные формы в цикле
Для Каждого СтрокаФормы Из ВыборВЗаказ Цикл
НоваяСтрока = ВТ.Добавить();
// Попытаемся найти ссылку по наименованию, если в форме была строка
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию(СтрокаФормы.Наименование);
НоваяСтрока.Цена = СтрокаФормы.Цена;
КонецЦикла;
// 3. Теперь ВТ можно смело передавать в запрос
Запрос.УстановитьПараметр("Таблица", ВТ);
Этот способ более трудозатратный для сервера, но он гарантирует, что в запрос попадет именно СправочникСсылка, а не текст.
Посмотрим на ситуацию со стороны СУБД. Когда мы используем конструкцию ПОМЕСТИТЬ во временную таблицу, передавая туда таблицу значений, 1С должна создать реальную таблицу в базе данных (SQL). Для этого ей нужно точно знать длину строк, точность чисел и типы ссылок.
Если в колонке ТЗ будет содержаться составной тип или тип ЛюбаяСсылка, выполнение запроса может завершиться ошибкой или привести к крайне низкой производительности. Поэтому хорошей практикой считается:
ОписаниеТипов одним конкретным типом для колонок, передаваемых в запрос.Новый КвалификаторыЧисла(15, 2).Неопределено, если колонка типизирована как Ссылка. Вместо этого должна быть пустая ссылка соответствующего типа (Справочники.Номенклатура.ПустаяСсылка()).Если ваша коллекция на форме является отображением табличной части документа или справочника, существует еще один профессиональный способ преобразования — использование метода ДанныеФормыВЗначение().
Разберем пример:
// Допустим, Объект - это реквизит формы с типом ДокументОбъект
ДокументОбъект = ДанныеФормыВЗначение(Объект, Тип("ДокументОбъект.ЗаказПокупателя"));
ТаблицаДляЗапроса = ДокументОбъект.Товары.Выгрузить();
// В ТаблицаДляЗапроса все типы колонок будут соответствовать метаданным документа
Этот метод работает быстрее ручного обхода и гарантирует 100% соответствие типов метаданным конфигурации. Однако он применим только тогда, когда данные на форме напрямую связаны с объектом базы данных.
Подведем итог нашему разбору. Чтобы не "терять" ссылки при работе с таблицами значений на управляемых формах:
СправочникСсылка, а не Строка.ТЗ = Коллекция.Выгрузить() создает новую таблицу. Старая переменная со всеми настройками типов будет затерта.Выгрузить() прямо в параметр запроса.Следуя этим правилам, вы обеспечите стабильную работу запросов и избежите трудноотлавливаемых ошибок, связанных с неявным приведением типов в 1С.