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