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