Почему запрос в коде при проведении документа не находит записи в регистре, хотя в консоли они есть?

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

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

Причина 1: Свойство документа «Удалять движения»

Первым делом рассмотрим настройки самого документа в конфигураторе. На вкладке «Движения» есть важное свойство — «Удалять движения». Чаще всего там установлено значение Удалять автоматически при проведении.

Выясним, что происходит на уровне платформы, когда пользователь нажимает кнопку «Провести»:

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

Почему же консоль запросов видит данные? Если вы запустили консоль параллельно и остановились отладчиком в коде, консоль может выполнять так называемое «грязное чтение» (в зависимости от СУБД и настроек изоляции), либо она обращается к данным вне контекста вашей транзакции. Однако внутри самого процесса проведения записи уже помечены как удаленные.

Причина 2: Данные еще не зафиксированы в базе данных

Рассмотрим обратную ситуацию: вы формируете новые движения в коде и тут же пытаетесь их прочитать запросом. Проанализируем стандартное поведение платформы: движения, которые вы записываете в коллекции Движения.ИмяРегистра, по умолчанию сбрасываются в таблицы СУБД только после успешного завершения процедуры ОбработкаПроведения.

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

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

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

Если вам нужно получить данные, которые документ собирается записать в регистр, обратитесь к его табличным частям. Рассмотрим пример изменения логики запроса:


// Вместо запроса к РегистрСведений.ПланКалендарныйГрафик
// Обращаемся к табличной части текущего документа
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ТЧ.НомерСтроки КАК НомерСтроки,
    |   ТЧ.ЗначениеПараметра
    |ИЗ
    |   Документ.АХ_ПланПродаж.Состав КАК ТЧ
    |ГДЕ
    |   ТЧ.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", ЭтотОбъект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();

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

Решение 2: Принудительная запись наборов записей

Если архитектура решения требует, чтобы запрос обязательно обращался именно к регистру (например, запрос сложный и соединяет данные нескольких регистров), мы можем заставить платформу записать движения в базу данных «здесь и сейчас», не дожидаясь окончания транзакции. Для этого используем метод Записать() для коллекции движений.

Рассмотрим последовательность действий:


// 1. Формируем движения
НоваяЗапись = Движения.ПланКалендарныйГрафик.Добавить();
НоваяЗапись.Период = Дата;
НоваяЗапись.Регистратор = Ссылка;
// ... заполнение других полей ...

// 2. Принудительно записываем движения в базу (внутри транзакции)
Движения.ПланКалендарныйГрафик.Записать();

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

Важно: Помните, что частый вызов метода Записать() внутри цикла или многократно в одном модуле может существенно снизить производительность системы, так как провоцирует лишние обращения к серверу БД.

Решение 3: Использование временных таблиц (Best Practice)

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

  1. Сначала данные из табличных частей документа подготавливаются и помещаются во временную таблицу ВТ_ДанныеДокумента.
  2. Затем эта временная таблица используется для расчетов и формирования движений.
  3. Если нужно сравнить новые данные со старыми (которые уже были в регистре), запрос строится к физической таблице регистра с исключением текущего документа, либо данные читаются до начала очистки движений.

Решение 4: Проверка прав доступа (RLS)

Иногда причина кроется не в транзакциях, а в правах доступа. Консоль запросов часто запускается под пользователем с полными правами, а код документа выполняется под рядовым сотрудником. Если на регистр настроены ограничения RLS (Record Level Security), запрос в коде может возвращать пустой результат просто потому, что пользователю запрещено читать эти записи.

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


УстановитьПривилегированныйРежим(Истина);
Результат = Запрос.Выполнить();
УстановитьПривилегированныйРежим(Ложь);

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

Резюме

Если ваш запрос не видит данные регистра при проведении:

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

← На главную