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