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