Почему в подписке ПриЗаписи справочника не обновляется контактная информация и как это исправить?

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

При разработке механизмов ведения истории реквизитов (например, ИНН, КПП и юридического адреса) в конфигурациях на базе обычных форм (в частности, «1С:Бухгалтерия предприятия 2.0» и отраслевых решениях на ее основе) разработчики часто сталкиваются с неожиданным поведением платформы: при попытке зафиксировать юридический адрес в подписке на событие ПриЗаписи справочников Контрагенты или Организации считываются старые данные адреса. Чтобы новые данные наконец записались в историю, пользователю приходится нажимать кнопку сохранения повторно.

Разберем детально, почему так происходит, как устроена последовательность записи данных в платформе «1С:Предприятие» и какие решения позволяют корректно фиксировать изменения.

Выясним причину: почему адрес остается старым

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

Когда пользователь работает с формой элемента справочника и нажимает кнопку «ОК» или «Записать», события выполняются в следующей последовательности:

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

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

Способ 1. Использование подписки на запись регистра сведений (рекомендуемый)

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

Рассмотрим особенности реализации этого подхода:

  1. Создаем подписку на событие ПриЗаписи для источника РегистрСведенийНаборЗаписей.КонтактнаяИнформация.
  2. В обработчике обязательно проверяем тип объекта-владельца через свойство отбора набора записей, так как в регистр пишется контактная информация множества других объектов системы (пользователей, физических лиц, складов).
  3. Для отслеживания ИНН и КПП сохраняем подписку на справочники Контрагенты и Организации. Если изменились только они, запись регистра контактной информации не вызывается. Если же изменился адрес — сработает подписка на регистр сведений.

Посмотрим на пример обработчика для набора записей контактной информации:


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

    // Проверяем наличие отбора по объекту
    Если Не Источник.Отбор.Найти("Объект") = Неопределено Тогда
        СсылкаНаОбъект = Источник.Отбор.Объект.Значение;
    Иначе
        Возврат;
    КонецЕсли;

    // Фильтруем типы: обрабатываем только Контрагентов и Организации
    ТипОбъекта = ТипЗнч(СсылкаНаОбъект);
    Если ТипОбъекта <> Тип("СправочникСсылка.Контрагенты") 
       И ТипОбъекта <> Тип("СправочникСсылка.Организации") Тогда
        Возврат;
    КонецЕсли;

    // Ищем строку с юридическим адресом в записываемом наборе
    НужныйВидАдреса = ?(ТипОбъекта = Тип("СправочникСсылка.Контрагенты"),
        Справочники.ВидыКонтактнойИнформации.ЮрАдресКонтрагента,
        Справочники.ВидыКонтактнойИнформации.ЮрАдресОрганизации);

    НовыйЮрАдрес = "";
    Для Каждого ЗаписьКИ Из Источник Цикл
        Если ЗаписьКИ.Вид = НужныйВидАдреса Тогда
            НовыйЮрАдрес = ЗаписьКИ.Представление;
            Прервать;
        КонецЕсли;
    КонецЦикла;

    // Фиксируем запись в регистре истории налоговых реквизитов
    // Получаем текущие ИНН и КПП непосредственно из ссылки объекта
    ОбъектРеквизиты = СсылкаНаОбъект;
    
    НаборИстории = РегистрыСведений.ИсторияНалоговыхРеквизитов.СоздатьНаборЗаписей();
    НаборИстории.Отбор.Объект.Установить(СсылкаНаОбъект);
    НаборИстории.Отбор.Период.Установить(ТекущаяДата());
    
    НоваяЗапись = НаборИстории.Добавить();
    НоваяЗапись.Период = ТекущаяДата();
    НоваяЗапись.Объект = СсылкаНаОбъект;
    НоваяЗапись.ИНН = ОбъектРеквизиты.ИНН;
    НоваяЗапись.КПП = ОбъектРеквизиты.КПП;
    НоваяЗапись.ЮридическийАдрес = НовыйЮрАдрес;
    
    НаборИстории.Записать(Истина);
КонецПроцедуры

Способ 2. Передача актуального адреса через ДополнительныеСвойства

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

Разберем логику взаимодействия формы и модуля объекта по шагам:

  1. В модуле формы элемента (в обработчике ПередЗаписью) считываем адрес непосредственно из элементов формы или локальной таблицы контактной информации, отображаемой пользователю.
  2. Помещаем это значение в структуру дополнительных свойств записываемого объекта:
    
    ЭтотОбъект.ДополнительныеСвойства.Вставить("ЮридическийАдресФормы", ТекущийЮрАдресНаФорме);
    
  3. В подписчике события ПриЗаписи справочника извлекаем переданное значение:
    
    Если Источник.ДополнительныеСвойства.Свойство("ЮридическийАдресФормы", ЮридическийАдрес) Тогда
        // Используем полученный из формы актуальный адрес
    Иначе
        // Если объект записывается программно, считываем значение из регистра
        ЮридическийАдрес = ПолучитьЮридическийАдресИзРегистра(Источник.Ссылка);
    КонецЕсли;
    

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

Ошибки в коде выборки контактной информации

Проанализируем распространенную ошибку при работе с методами общего модуля УправлениеКонтактнойИнформацией. Нередко в коде можно встретить следующую конструкцию:


НаборДанных = РегистрыСведений.ИсторияНалоговыхРеквизитов.СоздатьНаборЗаписей();
УправлениеКонтактнойИнформацией.ПрочитатьКонтактнуюИнформацию(НаборДанных, Объект);

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


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

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

← На главную