Разберем одну из самых распространенных и в то же время коварных ситуаций, с которой сталкиваются разработчики на платформе 1С:Предприятие 8. Представьте ситуацию: вы пишете запрос в модуле объекта документа (например, в процедуре ОбработкаПроведения), который должен выбрать данные из регистра по текущему регистратору. Вы уверены, что записи в регистре есть, консоль запросов при передаче той же ссылки это подтверждает, но код упорно возвращает пустой результат. Проанализируем, почему так происходит, и как правильно организовать работу с данными внутри транзакции.
Первым делом рассмотрим настройки самого документа в конфигураторе. На вкладке «Движения» есть важное свойство — «Удалять движения». Чаще всего там установлено значение Удалять автоматически при проведении.
Выясним, что происходит на уровне платформы, когда пользователь нажимает кнопку «Провести»:
ОбработкаПроведения.Почему же консоль запросов видит данные? Если вы запустили консоль параллельно и остановились отладчиком в коде, консоль может выполнять так называемое «грязное чтение» (в зависимости от СУБД и настроек изоляции), либо она обращается к данным вне контекста вашей транзакции. Однако внутри самого процесса проведения записи уже помечены как удаленные.
Рассмотрим обратную ситуацию: вы формируете новые движения в коде и тут же пытаетесь их прочитать запросом. Проанализируем стандартное поведение платформы: движения, которые вы записываете в коллекции Движения.ИмяРегистра, по умолчанию сбрасываются в таблицы СУБД только после успешного завершения процедуры ОбработкаПроведения.
Следовательно, любой запрос к физической таблице регистра внутри этой процедуры не увидит тех данных, которые еще находятся в оперативной памяти (в наборах записей). Это критически важный аспект транзакционной модели 1С.
Посмотрим на пример, как можно обойти эту проблему. Вместо того чтобы обращаться к регистру, который в данный момент находится в «неопределенном» состоянии (старое удалено, новое не записано), правильнее использовать данные самого объекта документа.
Если вам нужно получить данные, которые документ собирается записать в регистр, обратитесь к его табличным частям. Рассмотрим пример изменения логики запроса:
// Вместо запроса к РегистрСведений.ПланКалендарныйГрафик
// Обращаемся к табличной части текущего документа
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ.НомерСтроки КАК НомерСтроки,
| ТЧ.ЗначениеПараметра
|ИЗ
| Документ.АХ_ПланПродаж.Состав КАК ТЧ
|ГДЕ
| ТЧ.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ЭтотОбъект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Такой подход гарантирует, что вы работаете с актуальными данными, которые пользователь видит на экране, независимо от того, удалены ли уже движения или нет.
Если архитектура решения требует, чтобы запрос обязательно обращался именно к регистру (например, запрос сложный и соединяет данные нескольких регистров), мы можем заставить платформу записать движения в базу данных «здесь и сейчас», не дожидаясь окончания транзакции. Для этого используем метод Записать() для коллекции движений.
Рассмотрим последовательность действий:
// 1. Формируем движения
НоваяЗапись = Движения.ПланКалендарныйГрафик.Добавить();
НоваяЗапись.Период = Дата;
НоваяЗапись.Регистратор = Ссылка;
// ... заполнение других полей ...
// 2. Принудительно записываем движения в базу (внутри транзакции)
Движения.ПланКалендарныйГрафик.Записать();
// 3. Теперь запрос увидит эти данные
Запрос = Новый Запрос("ВЫБРАТЬ ... ИЗ РегистрСведений.ПланКалендарныйГрафик ГДЕ Регистратор = &Регистратор");
Запрос.УстановитьПараметр("Регистратор", Ссылка);
Результат = Запрос.Выполнить();
Важно: Помните, что частый вызов метода Записать() внутри цикла или многократно в одном модуле может существенно снизить производительность системы, так как провоцирует лишние обращения к серверу БД.
Проанализируем профессиональный подход к решению задачи. В типовых конфигурациях (например, ERP или УТ 11) практически никогда не делают запросы к регистрам текущего документа в момент проведения. Вместо этого используется механизм Менеджера Временных Таблиц.
ВТ_ДанныеДокумента.Иногда причина кроется не в транзакциях, а в правах доступа. Консоль запросов часто запускается под пользователем с полными правами, а код документа выполняется под рядовым сотрудником. Если на регистр настроены ограничения RLS (Record Level Security), запрос в коде может возвращать пустой результат просто потому, что пользователю запрещено читать эти записи.
Для проверки этой гипотезы попробуем выполнить запрос в привилегированном режиме:
УстановитьПривилегированныйРежим(Истина);
Результат = Запрос.Выполнить();
УстановитьПривилегированныйРежим(Ложь);
Если после этого данные появились — значит, необходимо пересмотреть настройки ролей и доступа к данному регистру.
Если ваш запрос не видит данные регистра при проведении:
Движения.ИмяРегистра.Записать(), если данные нужны в регистре немедленно.И помните: чудес в программировании 1С не бывает, есть только неописанные или неочевидные механизмы работы платформы с транзакциями СУБД.