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