В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда программисту необходимо проанализировать только что сформированные движения документа непосредственно в процессе его проведения. Однако при попытке выполнить запрос к виртуальным или физическим таблицам регистра (например, РегистрБухгалтерии.Хозрасчетный) результат оказывается пустым, хотя в отладке мы четко видим, что коллекция Движения заполнена проводками. Разберем детально, почему так происходит, и как правильно организовать работу с данными в этот момент.
Давайте проанализируем, что происходит в момент вызова процедуры ОбработкаПроведения. Когда документ начинает проводиться, система открывает транзакцию базы данных. Все изменения, которые мы вносим в коллекцию Движения, на данном этапе существуют только в оперативной памяти сервера приложений. Они представляют собой набор записей, который еще не был передан СУБД (SQL-серверу) для физической записи в таблицы.
Объект Запрос в 1С всегда обращается напрямую к таблицам базы данных. Поскольку транзакция еще не завершена и метод записи набора не вызывался, физические таблицы регистра еще не содержат новых строк. Именно поэтому ваш запрос возвращает пустой результат. Вы пытаетесь прочитать из базы то, чего там физически еще нет, хотя логически (в контексте объекта документа) эти данные уже сформированы.
Самый правильный и производительный способ — это работа с коллекцией Движения напрямую, не обращаясь к базе данных. Если нам нужно проанализировать сформированные проводки, мы можем выгрузить их в таблицу значений и обработать. Рассмотрим пример, как это сделать:
// Внутри процедуры ОбработкаПроведения
// Выгружаем движения регистра Хозрасчетный в таблицу значений
ТаблицаПроводок = Движения.Хозрасчетный.Выгрузить();
// Теперь мы можем работать с данными с помощью методов таблицы значений
// Например, найти нужные нам счета или суммы
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("СчетКт", ПланыСчетов.Хозрасчетный.РасчетыСПоставщикамиИПодрядчиками);
НайденныеСтроки = ТаблицаПроводок.НайтиСтроки(ПараметрыОтбора);
СуммаИтого = 0;
Для Каждого СтрокаПроводки Из НайденныеСтроки Цикл
СуммаИтого = СуммаИтого + СтрокаПроводки.Сумма;
КонецЦикла;
Этот метод хорош тем, что он не создает лишней нагрузки на SQL-сервер и исключает риск возникновения взаимных блокировок (Deadlocks), так как мы работаем исключительно с локальными данными сеанса.
Если логика анализа данных слишком сложна для простых циклов и вам обязательно нужен язык запросов (например, для соединения с другими справочниками или регистрами), мы можем использовать выгруженную таблицу значений как источник для запроса через Менеджер временных таблиц. Рассмотрим этот алгоритм по шагам:
Движения.Хозрасчетный.Выгрузить().Посмотрим на пример кода:
ТаблицаДвижений = Движения.Хозрасчетный.Выгрузить();
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Передаем таблицу в запрос
Запрос.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Движения ИЗ &ТЗ КАК ТЗ;
|ВЫБРАТЬ
| ВТ.СчетДт,
| ВТ.Сумма
|ИЗ ВТ_Движения КАК ВТ
|ГДЕ ВТ.СчетКт = &НужныйСчет";
Запрос.УстановитьПараметр("ТЗ", ТаблицаДвижений);
Запрос.УстановитьПараметр("НужныйСчет", ПланыСчетов.Хозрасчетный.Касса);
Результат = Запрос.Выполнить();
Существует еще один способ — заставить систему записать движения в базу данных прямо посреди транзакции, не дожидаясь окончания процедуры проведения. Для этого используется метод Записать() у конкретного набора записей. После этого вызова обычный запрос к таблицам регистра начнет "видеть" эти данные.
Важное предупреждение: используйте этот метод с осторожностью! Принудительная запись может привести к снижению производительности и преждевременным блокировкам таблиц БД, что критично в высоконагруженных системах.
// Принудительно записываем движения в БД в рамках текущей транзакции
Движения.Хозрасчетный.Записать();
// Теперь ваш исходный запрос из сообщения сработает
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ИЗ РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(...)";
// Установка параметров...
Выборка = Запрос.Выполнить().Выбрать();
Обратите внимание, что если у документа в свойстве "Записывать выбранные" стоит значение "Записывать выбранные", то в конце процедуры проведения система попытается записать набор еще раз. Если вы уже вызвали Записать() вручную, убедитесь, что вы не очистили коллекцию и не изменили её состояние непреднамеренно.
При использовании запросов в проведении также важно понимать разницу между таблицами. В тексте запроса автора используется виртуальная таблица ДвиженияССубконто. Проанализируем ситуацию: эта таблица является "разверткой" физической таблицы проводок по субконто. Если вы используете Решение 3 (принудительную запись), помните, что расчет итогов по виртуальным таблицам остатков и оборотов может быть не всегда актуален до завершения транзакции (из-за механизма разделения итогов), но для таблицы движений (проводок) данные должны появиться сразу после вызова метода Записать().
Для эффективной и безопасной разработки в 1С следуйте этим правилам:
ОбработкаПроведения или при явном вызове Записать().Движения.ИмяРегистра содержит актуальные данные "здесь и сейчас".Таким образом, для решения вашей задачи лучше всего выгрузить коллекцию Движения.Хозрасчетный в таблицу значений и выполнить необходимые расчеты или проверки программным кодом, не прибегая к медленным запросам к базе данных.