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