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