Почему документ «Корректировка регистров» попадает в план обмена без движений и как проверить наличие у него реквизита?

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

При работе с планами обмена данными в 1С разработчики часто сталкиваются с ситуацией, когда в таблицу изменений попадают документы, которые, на первый взгляд, не должны там быть. Классический пример — документ «Корректировка регистров», который может быть зарегистрирован для обмена, даже если в нем нет ни одной записи по нужному регистру накопления. Это вызывает два основных вопроса: почему так происходит, и как безопасно работать с таким документом-регистратором, если его структура (например, наличие реквизита «Партнер») заранее неизвестна?

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

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

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

Проанализируем типичный сценарий:

  1. Документ (например, «Реализация товаров и услуг» или та же «Корректировка регистров») был проведен и создал движения в регистре РасчетыСКлиентами. В этот момент платформа зарегистрировала изменение документа для всех узлов обмена.
  2. Позже этот документ был перепроведен или изменен таким образом, что все его движения по регистру РасчетыСКлиентами были удалены. Набор записей стал пустым.
  3. С точки зрения платформы, документ снова был изменен. Она обязана зафиксировать этот факт. Для чего? Чтобы при обмене данными узел-получатель «узнал» об этом изменении и тоже удалил у себя движения по этому регистратору, которые он получил ранее.

Таким образом, появление документа с пустым набором записей в таблице изменений — это не ошибка, а корректное поведение системы, обеспечивающее целостность данных между базами. Система сообщает: «Состояние этого документа изменилось, примените это изменение у себя». Для узла-получателя это означает, что нужно удалить ранее загруженные записи.

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

Проблема вторая: как безопасно обратиться к реквизиту регистратора

Когда мы получаем из таблицы изменений регистратор, его тип может быть разным: «РеализацияТоваровУслуг», «ПоступлениеТоваровУслуг», «КорректировкаРегистров» и т.д. Попытка прямого обращения к реквизиту, который есть не у всех, приведет к ошибке.


// Такой код вызовет ошибку, если у СсылкаНаРегистратор нет реквизита "Партнер"
СсылкаНаРегистратор = РезультатЗапроса.Регистратор;
Партнер = СсылкаНаРегистратор.Партнер; // Поле объекта не обнаружено (Партнер)

Рассмотрим четыре способа, как решить эту проблему, от наиболее предпочтительного к менее желательному.

Решение 1: Использование типовой функции (рекомендуемый способ)

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

Функция ЕстьРеквизитИлиСвойствоОбъекта из общего модуля ОбщегоНазначенияКлиентСервер создана специально для таких задач. Она универсальна и проверяет наличие не только реквизитов шапки, но и других свойств объекта.

Пример кода:


СсылкаНаРегистратор = РезультатЗапроса.Регистратор;

Если ОбщегоНазначенияКлиентСервер.ЕстьРеквизитИлиСвойствоОбъекта(СсылкаНаРегистратор, "Партнер") Тогда
    // Мы уверены, что реквизит существует, можно смело к нему обращаться
    Партнер = СсылкаНаРегистратор.Партнер;
    Сообщить("Для документа " + СсылкаНаРегистратор + " найден партнер: " + Партнер);
Иначе
    Сообщить("У документа " + СсылкаНаРегистратор + " реквизит 'Партнер' отсутствует.");
КонецЕсли;

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

Решение 2: Проверка через метаданные (универсальный способ)

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

Пример кода:


СсылкаНаРегистратор = РезультатЗапроса.Регистратор;

// Получаем объект метаданных для нашего документа
МетаданныеДокумента = СсылкаНаРегистратор.Метаданные();

// Ищем в коллекции реквизитов нужный нам по имени
Если МетаданныеДокумента.Реквизиты.Найти("Партнер") <> Неопределено Тогда
    // Реквизит найден в описании объекта
    Партнер = СсылкаНаРегистратор.Партнер;
    Сообщить("Партнер (через метаданные): " + Партнер);
Иначе
    Сообщить("Реквизит 'Партнер' в метаданных не найден.");
КонецЕсли;

Этот способ абсолютно надежен, так как он оперирует структурой объекта, определенной в конфигураторе. Он немного более "низкоуровневый", но дает полный контроль.

Решение 3: Использование метода «ЗаполнитьЗначенияСвойств» (хитрый способ)

Это элегантный и нетривиальный способ, который позволяет получить значение свойства, не вызывая ошибки, если его не существует. Суть метода в том, что мы пытаемся скопировать свойства из объекта-источника в структуру-приемник. Если в источнике есть свойство с именем, совпадающим с ключом структуры, значение будет скопировано. Если нет — метод просто проигнорирует этот ключ.

Пример кода:


СсылкаНаРегистратор = РезультатЗапроса.Регистратор;

// Создаем структуру с ключом, имя которого совпадает с искомым реквизитом
СтруктураДляПроверки = Новый Структура("Партнер");

// Пытаемся заполнить структуру из объекта. Ошибки не будет.
ЗаполнитьЗначенияСвойств(СтруктураДляПроверки, СсылкаНаРегистратор);

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

Важный нюанс: этот метод не позволяет отличить ситуацию, когда реквизита нет, от ситуации, когда реквизит есть, но его значение пустое (NULL, пустая ссылка, 0). Это может быть как преимуществом, так и недостатком, в зависимости от логики вашей задачи.

Решение 4: Конструкция «Попытка...Исключение» (нежелательный способ)

Использование блока Попытка...Исключение для проверки наличия реквизита — работающий, но с точки зрения архитектуры плохой подход. Обработка исключений — это медленный механизм, предназначенный для отлова непредвиденных ошибок (проблемы с файлами, внешними компонентами и т.д.), а не для управления нормальной логикой программы.

Пример кода:


СсылкаНаРегистратор = РезультатЗапроса.Регистратор;
Партнер = Неопределено;

Попытка
    Партнер = СсылкаНаРегистратор.Партнер;
    // Этот код выполнится, только если реквизит существует
Исключение
    // Сюда мы попадем, если обращение к СсылкаНаРегистратор.Партнер вызвало ошибку
    Сообщить("Обращение к реквизиту 'Партнер' вызвало исключение.");
КонецПопытки;

Если Партнер <> Неопределено Тогда
    Сообщить("Партнер (через Попытку): " + Партнер);
КонецЕсли;

Использовать этот метод стоит только в крайнем случае, когда другие способы по каким-либо причинам недоступны. Он делает код менее читаемым и может скрыть другие, более серьезные ошибки выполнения.

← На главную