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