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