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