Как добиться регистрации изменений табличной части "Дополнительные реквизиты" объекта "Номенклатура" при обмене между 1С КА2 и Розница 2?

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

При организации обмена данными между различными конфигурациями 1С, такими как 1С:Комплексная автоматизация 2 (КА2) и 1С:Розница 2, мы часто сталкиваемся с ситуацией, когда изменения в основных реквизитах объекта успешно регистрируются и передаются. Однако, если изменения затрагивают только табличную часть, особенно такие служебные, как "Дополнительные реквизиты", то стандартные механизмы авторегистрации могут их проигнорировать. В этой статье мы подробно разберем, почему так происходит и как мы можем решить эту задачу.

Почему стандартная авторегистрация не работает в нашем случае?

Прежде чем перейти к решению, давайте выясним причину такого поведения. Механизмы обмена данными в 1С, особенно те, что реализованы с использованием Библиотеки стандартных подсистем (БСП), обычно основываются на сравнении версий объекта или явной фиксации изменений его основных реквизитов. Когда мы изменяем обычный реквизит объекта (например, "Наименование" или "Артикул"), система 1С автоматически устанавливает флаг модифицированности для объекта, что приводит к его регистрации для обмена.

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

Таким образом, наша задача заключается в том, чтобы либо явно "сообщить" системе, что объект "Номенклатура" был изменен из-за модификации его "Дополнительных реквизитов", либо принудительно зарегистрировать его для обмена.

Способ 1: Явная установка флага модифицированности объекта

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

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

Пример концептуального кода для обработчика подписки:


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

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

    // Флаг для определения, были ли изменены дополнительные реквизиты
    ИзмененыДопРеквизиты = Ложь;

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

    Если ИзмененыДопРеквизиты Тогда
        // Устанавливаем флаг модифицированности для объекта
        // Это может привести к его регистрации для обмена
        Источник.УстановитьМодифицированность(Истина);
    КонецЕсли;

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

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

Способ 2: Программная регистрация изменений (Рекомендуемый подход)

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

  1. Создание подписки на событие

    Мы начинаем с создания новой ПодпискаНаСобытие в конфигураторе. Нам потребуется указать:

    • Имя: Осмысленное имя, например, ПриЗаписиНоменклатурыРегистрацияДопРеквизитов.
    • Источник:

      Документ.Номенклатура

      (или Справочник.Номенклатура, если это справочник).
    • Событие:

      ПередЗаписью

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

    В модуле обработчика мы реализуем основную логику. Нам нужно будет выполнить следующие шаги:

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

        : Регистрирует указанный Объект для обмена с конкретным УзелПланаОбмена.
      • ПланыОбменаМенеджер.ЗарегистрироватьИзменения(УзелПланаОбмена, СсылкаНаОбъект)

        : Альтернативный метод, который также регистрирует изменения.

      Мы должны перебрать все узлы плана обмена (например, ПланыОбмена.Полный.ПолучитьВсеУзлы()) или только те, с которыми у нас настроен обмен (в нашем случае, узел "Розница 2").

Пример кода для обработчика подписки (в общем модуле):


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

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

    // Флаг, который будет установлен, если обнаружены изменения в "Дополнительных реквизитах"
    ИзмененыДопРеквизиты = Ложь;

    // Проводим сравнение табличных частей "ДополнительныеРеквизиты"
    Если СтараяНоменклатура <> Неопределено Тогда
        // Сравниваем количество строк. Разное количество - явное изменение.
        Если СтараяНоменклатура.ДополнительныеРеквизиты.Количество() <> Источник.ДополнительныеРеквизиты.Количество() Тогда
            ИзмененыДопРеквизиты = Истина;
        Иначе
            // Если количество строк одинаково, сравниваем каждую строку по содержимому.
            // Сначала проверяем, есть ли в новом наборе что-то, чего нет в старом, или что изменилось.
            Для Каждого СтрокаНовая Из Источник.ДополнительныеРеквизиты Цикл
                // Ищем строку в старой ТЧ по полю "Свойство".
                // Предполагаем, что "Свойство" уникально в рамках одной ТЧ для ДопРеквизитов.
                НайденнаяСтрокаСтарая = СтараяНоменклатура.ДополнительныеРеквизиты.Найти(СтрокаНовая.Свойство, "Свойство");

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

            // Если изменений пока не найдено, проверяем, не были ли удалены какие-либо строки.
            // Для этого смотрим, есть ли в старом наборе что-то, чего нет в новом.
            Если Не ИзмененыДопРеквизиты Тогда
                 Для Каждого СтрокаСтарая Из СтараяНоменклатура.ДополнительныеРеквизиты Цикл
                     НайденнаяСтрокаНовая = Источник.ДополнительныеРеквизиты.Найти(СтрокаСтарая.Свойство, "Свойство");
                     Если НайденнаяСтрокаНовая = Неопределено Тогда // Строка из старой ТЧ не найдена в новой - значит, она была удалена
                         ИзмененыДопРеквизиты = Истина;
                         Прервать;
                     КонецЕсли;
                 КонецЦикла;
            КонецЕсли;
        КонецЕсли;
    Иначе
        // Если старый объект не удалось получить (например, при создании нового элемента)
        // Но у нового объекта есть Дополнительные реквизиты, мы считаем это изменением,
        // которое требует регистрации.
        Если Источник.ДополнительныеРеквизиты.Количество() > 0 Тогда
            ИзмененыДопРеквизиты = Истина;
        КонецЕсли;
    КонецЕсли;

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

Важные замечания к коду:

Дополнительные реквизиты и их хранение

Важно понимать, что "Дополнительные реквизиты" в 1С (в отличие от "Дополнительных сведений") хранятся непосредственно в табличной части самого объекта. Это означает, что они являются его неотъемлемой частью, хотя и могут управляться через универсальный механизм БСП.

Функционал использования дополнительных реквизитов и сведений активируется в разделе "НСИ и администрирование – Администрирование – Общие настройки". Там же настраиваются сами виды дополнительных реквизитов для различных объектов. Именно то, что они встроены в объект как табличная часть, и является причиной, почему их изменения не всегда "всплывают" на уровень регистрации основного объекта.

Диагностика и отладка

Для проверки того, как работает ваша регистрация, и для диагностики возможных проблем, мы можем использовать стандартные инструменты 1С:

  1. Журнал регистрации: В нем будут отображаться события записи объектов, а также действия по регистрации изменений для обмена (если включено соответствующее логирование).
  2. Обработка "Регистрация изменений для обмена": Это внешняя или встроенная обработка (часто поставляется с БСП), которая позволяет просматривать, какие объекты зарегистрированы для конкретного узла обмена. Мы можем вручную добавлять или удалять объекты из списка регистрации, что полезно для тестирования и выборочной отладки.
  3. Отладчик: Стандартный отладчик 1С поможет пошагово проанализировать выполнение нашего кода в обработчике подписки, проверить значения переменных, а также убедиться, что условия сравнения срабатывают корректно.

Решение проблемы с регистрацией изменений в табличной части "Дополнительные реквизиты" объекта "Номенклатура" при обмене данными между 1С КА2 и Розница 2 требует программного вмешательства. Наиболее эффективным подходом является создание подписки на событие ПередЗаписью объекта Номенклатура и реализация в ней логики по сравнению табличных частей с последующей явной регистрацией объекта для обмена. Этот подход обеспечивает полный контроль над процессом и гарантирует корректную передачу важных данных между вашими информационными базами.

← На главную