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

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

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

Почему данные не передаются через ДополнительныеСвойства?

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

Способ 1. Получение старых данных через запрос по отбору

Это самый надежный и часто используемый метод. Вместо того чтобы пытаться «передать» старое значение из первого вызова во второй, мы можем самостоятельно считать его из базы данных в тот момент, когда это необходимо. Рассмотрим ситуацию: нам нужно зафиксировать в Журнале Регистрации, что именно было удалено или изменено.

Выясним причину, по которой это удобно делать в первом вызове ПередЗаписью. В этот момент ЭтотОбъект.Количество() равно 0 (если идет полная замена набора), но в базе данных записи еще физически существуют. Мы можем использовать ЭтотОбъект.Отбор, чтобы понять, какие именно данные сейчас будут стерты.

Разберем пример кода в модуле набора записей:


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

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

Важный момент: функция ПолучитьУсловияИзОтбора должна динамически формировать текст условия ГДЕ на основании элементов коллекции ЭтотОбъект.Отбор. Это универсальный подход, который позволяет не привязываться к конкретным измерениям.

Способ 2. Использование параметров сеанса для передачи контекста

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

Параметры сеанса глобальны для текущего пользователя и сохраняются на протяжении всей работы. Однако следует соблюдать осторожность: запись в параметры сеанса — это транзакционная операция, которая может излишне нагружать сервер при массовых записях в регистр. Мы рекомендуем использовать этот метод только для хранения уникальных идентификаторов или временных структур типа ФиксированнаяСтруктура.

Рассмотрим алгоритм действий:

  1. В первом вызове ПередЗаписью (удаление) сохраняем нужные данные в параметр сеанса (например, с типом ХранилищеЗначения или предварительно описанной структурой).
  2. Во втором вызове ПередЗаписью (запись новых) считываем данные из параметра сеанса.
  3. Обязательно очищаем параметр сеанса после использования, чтобы не повлиять на следующие операции записи.

Способ 3. Перенос логики в подписки на события

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

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

Способ 4. Встроенное версионирование (БСП)

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

Посмотрим на шаги настройки:

  1. Переходим в раздел администрирования настроек программы.
  2. Находим пункт «Поддержка и обслуживание» -> «Версионирование объектов».
  3. Включаем версионирование для нужного регистра сведений.

Система сама будет создавать «снимки» набора записей до и после изменения. Эти данные хранятся в регистре СведенияОВерсияхОбъектов. Это наиболее «цивилизованный» способ решения задачи в современных типовых конфигурациях (ERP, УТ 11, БП 3.0), так как он учитывает все нюансы транзакционности и производительности.

Особенности логирования в Журнал Регистрации

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

Поэтому мы рекомендуем придерживаться следующей стратегии:

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

Подведем итог. Передача переменной напрямую между двумя вызовами ПередЗаписью невозможна из-за архитектуры платформы. Оптимальным решением является повторное чтение данных из БД по отбору набора или использование событий ПриЗаписи в сочетании с подписками на события. Это обеспечит чистоту кода и надежность вашего механизма логирования.

← На главную