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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

Особенности работы набора записей регистра накопления

Для начала проанализируем, как 1С работает с движениями. Важно понимать, что регистр накопления всегда оперирует наборами записей с фильтром по регистратору. Когда документ проводится, отменяется или перепроводится, платформа выполняет запись набора записей. Рассмотрим два основных сценария:

  1. Запись новых движений: В набор записей добавляются строки, и метод Записать() вызывается с параметром Замещение = Истина.
  2. Удаление существующих движений (отмена проведения): В базу записывается пустой набор записей с тем же регистратором и параметром Замещение = Истина.

Именно второй пункт часто сбивает с толку. В событии ПриЗаписи, когда мы пытаемся проанализировать Источник, мы видим, что количество записей равно нулю. Это логично, так как мы записываем «пустоту», чтобы затереть то, что было в базе. Но как узнать, что именно было удалено? Выясним это далее.

Шаг 1. Перехват данных в событии ПередЗаписью

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

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

  1. Проверяем флаг ОбменДанными.Загрузка, чтобы избежать лишней логики при загрузке данных.
  2. Получаем Регистратор из отбора набора записей.
  3. Выполняем запрос к таблице регистра накопления, чтобы получить текущие записи, которые еще физически находятся в СУБД.
  4. Сохраняем эти данные во временное хранилище, чтобы использовать их в событии ПриЗаписи. Для этого идеально подходит свойство ДополнительныеСвойства объекта Источник.

Посмотрим на пример кода для обработчика ПередЗаписью:


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

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

Шаг 2. Сравнение и обработка в событии ПриЗаписи

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

Проанализируем ситуацию:

Пример кода для обработки изменений:


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

    // Извлекаем сохраненные ранее данные
    СтарыеЗаписи = Неопределено;
    Источник.ДополнительныеСвойства.Свойство("СтарыеЗаписи", СтарыеЗаписи);
    
    НовыеЗаписи = Источник.Выгрузить();
    
    // Здесь мы можем сравнить две таблицы (СтарыеЗаписи и НовыеЗаписи)
    // и определить, какие именно строки были добавлены, изменены или удалены.
    
    Если НовыеЗаписи.Количество() = 0 И (СтарыеЗаписи <> Неопределено И СтарыеЗаписи.Количество() > 0) Тогда
        // Сценарий: Отмена проведения (удаление всех записей)
        ЗарегистрироватьИзмененияДляВнешнейСистемы(СтарыеЗаписи, "Удаление");
    ИначеЕсли НовыеЗаписи.Количество() > 0 Тогда
        // Сценарий: Проведение или перепроведение
        ЗарегистрироватьИзмененияДляВнешнейСистемы(НовыеЗаписи, "Запись");
    КонецЕсли;

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

Важные нюансы и вопросы транзакционности

Разберем критически важный момент, касающийся синхронизации с внешними программами. Никогда не выполняйте прямые HTTP-запросы или вызовы API сторонних сервисов непосредственно внутри кода подписки на события.

Почему это опасно? Подписки выполняются внутри транзакции записи документа. Если после записи вашего регистра произойдет ошибка в другом регистре или сработает какая-то проверка (например, контроль остатков), транзакция будет откачена. Однако ваш внешний сервис уже получит уведомление о том, что данные записаны. В итоге возникнет рассинхронизация: во внешней базе данные есть, а в 1С — нет.

Как правильно? Вместо прямой отправки используйте «Таблицу регистрации изменений» или «Очередь сообщений»:

  1. В подписке ПриЗаписи фиксируйте факт изменения в специальном регистре сведений (стеке).
  2. Запускайте отдельное регламентное задание, которое будет считывать накопленные изменения и отправлять их во внешнюю систему.
  3. Регламентное задание увидит записи только тогда, когда транзакция в 1С будет успешно завершена (зафиксирована).

Использование параметра «Замещение»

Проанализируем также параметр Замещение в событии ПередЗаписью. В большинстве случаев при проведении документов он равен Истина. Однако, если вы программно записываете набор записей и устанавливаете Замещение = Ложь, новые строки просто добавятся к существующим в базе. В этом случае при чтении «старых» данных в ПередЗаписью и получении «новых» данных в ПриЗаписи нужно учитывать, что итоговый результат в базе будет суммой этих двух наборов. Всегда проверяйте этот флаг, если ваша логика подразумевает частичное обновление движений.

Заключение

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

← На главную