Как проверить изменение реквизитов и счетов учета в событии ПередЗаписью в 1С?

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет IT и автоматизация бизнеса
← На главную

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

Особенности работы ссылки и объектной модели в 1С

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

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

Важно помнить разницу между объектом в оперативной памяти и записью в таблице базы данных:

  1. Объект в памяти (Источник / ЭтотОбъект): содержит новые, отредактированные пользователем или кодом значения реквизитов, которые еще не зафиксированы в таблицах базы данных.
  2. Запись в базе данных (Источник.Ссылка): хранит старые значения реквизитов, актуальные на момент до начала текущей транзакции записи.
  3. Запросы к базе данных: любой запрос в 1С считывает данные непосредственно из физических таблиц СУБД. Если передать в запрос Источник.Ссылка, запрос прочитает старое состояние объекта из базы, а не его текущие значения из оперативной памяти.

Способ 1. Сравнение старых и новых значений реквизитов в памяти

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

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


Процедура ПроверитьИзменениеПередЗаписью(Источник, Отказ) Экспорт
    
    // Если объект новый, старых данных в базе нет
    Если Источник.ЭтоНовый() Тогда
        Возврат;
    КонецЕсли;
    
    // Получаем текущие значения из памяти и старые значения из базы данных
    НовыйРодитель = Источник.Родитель;
    СтарыйРодитель = Источник.Ссылка.Родитель;
    
    НовыйВид = Источник.ВидНоменклатуры;
    СтарыйВид = Источник.Ссылка.ВидНоменклатуры;
    
    // Проверяем факт изменения ключевых полей
    Если НовыйРодитель <> СтарыйРодитель Или НовыйВид <> СтарыйВид Тогда
        // Реквизиты изменились, выполняем дальнейшую логику
    КонецЕсли;
    
КонецПроцедуры

Обратите внимание: проверка условия Источник.Родитель <> Источник.Ссылка.Родитель является общепринятым стандартом платформы, так как читается проще и выполняется быстрее, чем отрицание с оператором равенства.

Способ 2. Передача измененных значений напрямую в параметры запроса

Если алгоритм определения счетов учета завязан на запрос к регистрам сведений с учетом иерархии номенклатуры, не следует обращаться в тексте запроса через точку от ссылки (например, ГДЕ Номенклатура.Родитель = ...). Вместо этого передадим новое значение родителя параметром запроса.

Разберем пример модификации запроса:


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

Запрос.УстановитьПараметр("Период", ТекущаяДата());
// Передаем значение из памяти объекта, а не из ссылки базы
Запрос.УстановитьПараметр("РодительНоменклатуры", Источник.Родитель);

Результат = Запрос.Выполнить();

Способ 3. Передача таблицы значений в запрос через временную таблицу

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

Рассмотрим реализацию:


ТЗ_Объект = Новый ТаблицаЗначений;
ТЗ_Объект.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ_Объект.Колонки.Добавить("Родитель", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ_Объект.Колонки.Добавить("ВидНоменклатуры", Новый ОписаниеТипов("СправочникСсылка.ВидыНоменклатуры"));

СтрокаТЗ = ТЗ_Объект.Добавить();
СтрокаТЗ.Ссылка = Источник.Ссылка;
СтрокаТЗ.Родитель = Источник.Родитель;
СтрокаТЗ.ВидНоменклатуры = Источник.ВидНоменклатуры;

МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.Ссылка КАК Номенклатура,
    |	ТЗ.Родитель КАК Родитель,
    |	ТЗ.ВидНоменклатуры КАК ВидНоменклатуры
    |ПОМЕСТИТЬ ВТ_ТекущиеДанные
    |ИЗ
    |	&ТЗ_Объект КАК ТЗ";

Запрос.УстановитьПараметр("ТЗ_Объект", ТЗ_Объект);
Запрос.Выполнить();

// Далее соединяем ВТ_ТекущиеДанные с регистрами сведений

Способ 4. Передача данных между ПередЗаписью и ПриЗаписи через ДополнительныеСвойства

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

Разберем по шагам:

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

Посмотрим на пример кода:


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

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

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

← На главную