Как с помощью подписки на событие записать дополнительный реквизит в регистр накопления?

Программист Управленческий учет Торговля и дистрибуция
← На главную

В процессе разработки в среде 1С часто возникает задача обогащения данных регистров накопления информацией, которая не заполняется стандартными механизмами проведения документа. Например, при проведении документа ПеремещениеТоваров нам может потребоваться заполнить специфический реквизит регистра (допустим, «МОЛ» — материально ответственное лицо), данные о котором хранятся в шапке документа или вычисляются по сложной логике. Рассмотрим, как это сделать правильно и эффективно, используя механизм подписок на события.

Выбор подходящего события для подписки

Для решения нашей задачи мы должны использовать подписку на событие для объекта типа РегистрНакопленияНаборЗаписей. Многие начинающие разработчики пытаются использовать подписки на события самого документа (например, ПриЗаписи или ОбработкаПроведения), однако это не всегда удобно, если нужно гарантированно модифицировать движения регистра, независимо от того, как они формируются.

Разберем, почему оптимальным выбором является событие ПередЗаписью набора записей регистра накопления:

  1. Оно срабатывает непосредственно перед тем, как данные попадут в базу данных.
  2. В этот момент набор записей уже сформирован документом-регистратором.
  3. Мы имеем прямой доступ к коллекции строк, которые будут записаны.

Создание и настройка подписки на событие

Проанализируем процесс создания подписки по шагам. Сначала в дереве конфигурации необходимо создать новый объект Подписка на событие. В качестве источника выберем конкретный регистр накопления (его набор записей), а в качестве события — ПередЗаписью. В качестве обработчика укажем процедуру в общем модуле (важно, чтобы модуль имел свойство «Сервер»).

Рассмотрим структуру обработчика, который автоматически создаст платформа:


Процедура ПередЗаписьюРегистраНакопления(Источник, Отказ, Замещение) Экспорт
    // Источник — это РегистрНакопленияНаборЗаписей
    // Отказ — булево, позволяющее прервать запись
    // Замещение — булево, указывающее на перезапись существующих движений
КонецПроцедуры

Реализация логики заполнения реквизита

Теперь выясним причину, по которой нам нужно быть осторожными при написании кода. Подписка на набор записей срабатывает не только при проведении документа, но и при его отмене или простом удалении движений. Поэтому нам необходимо добавить несколько проверок.

Во-первых, проверим, не является ли запись набора пустой. При отмене проведения или перед записью новых движений платформа сначала записывает пустой набор записей, чтобы очистить старые данные. В этом случае нам нечего заполнять.

Во-вторых, проверим тип регистратора. Подписка может быть назначена на весь регистр, но логика заполнения «МОЛ» может относиться только к документу ПеремещениеТоваров.

Разберем пример эффективного кода:


Процедура ПередЗаписьюРегистраНакопления(Источник, Отказ, Замещение) Экспорт
    
    // 1. Игнорируем запись при обмене данными
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // 2. Проверяем, что есть записи для обработки
    Если Источник.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    // 3. Получаем регистратор из отбора набора записей
    РегистраторСсылка = Источник.Отбор.Регистратор.Значение;

    // 4. Проверяем тип документа (если подписка общая для нескольких типов)
    Если ТипЗнч(РегистраторСсылка) <> Тип("ДокументСсылка.ПеремещениеТоваров") Тогда
        Возврат;
    КонецЕсли;

    // 5. Получаем необходимые данные (МОЛ) из документа. 
    // Рекомендуется использовать запрос для производительности.
    ЗначениеМОЛ = ПолучитьДанныеМОЛИзДокумента(РегистраторСсылка);

    // 6. Заполняем реквизит в каждой строке набора
    Для Каждого Запись Из Источник Цикл
        Запись.МОЛ = ЗначениеМОЛ;
    КонецЦикла;

КонецПроцедуры

Оптимизация получения данных

Посмотрим на этап получения данных из документа. Использование метода ПолучитьОбъект() в подписках — плохая практика, так как это приводит к избыточному чтению всех данных документа из базы. Проанализируем более профессиональный подход с использованием запроса.


Функция ПолучитьДанныеМОЛИзДокумента(СсылкаНаДокумент)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Док.Ответственный КАК МОЛ
        |ИЗ
        |   Документ.ПеремещениеТоваров КАК Док
        |ГДЕ
        |   Док.Ссылка = &Ссылка";
    
    Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
    РезультатЗапроса = Запрос.Выполнить();
    
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.МОЛ;
    КонецЕсли;
    
    Возврат Справочники.ФизическиеЛица.ПустаяСсылка();
КонецФункции

Нюансы двойного срабатывания подписки

Как отмечалось в обсуждении на форуме, при перепроведении документа подписка срабатывает дважды. Рассмотрим этот процесс подробнее:

  1. Первый вызов: Платформа записывает пустой набор записей с установленным отбором по регистратору, чтобы «стереть» старые движения в базе. На этом этапе Источник.Количество() будет равно 0. Благодаря нашей проверке в начале процедуры, код просто завершит работу, не тратя ресурсы.
  2. Второй вызов: Документ сформировал новые движения и передает их для записи. Теперь Источник.Количество() больше нуля, и наш цикл успешно проставит значение «МОЛ» во все строки.

Таким образом, использование подписки ПередЗаписью набора записей является надежным способом внедрения в процесс проведения. Это гарантирует, что данные будут записаны корректно даже при программном формировании движений или использовании сторонних обработок, которые вызывают метод Записать() у набора записей регистра.

Резюме для разработчика

Для успешной реализации задачи всегда помните о следующих моментах:

Такой подход делает ваш код чистым, быстрым и устойчивым к изменениям в типовой конфигурации, так как вам не приходится вносить изменения непосредственно в модуль проведения документа.

← На главную