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