Задача: остатки товаров на складе на дату
Вторая классическая задача с собеседования: показать остатки номенклатуры по складам на конкретную дату — и не запутаться с параметром Период и отбором положительных остатков.
Остаток на дату — сколько единиц товара числилось на складе на определённый момент времени. Это накопленный итог всех приходов и расходов до этой даты, который платформа отдаёт готовым из виртуальной таблицы
Остатки.
Если предыдущая задача была про деньги, то эта — про товары, но механика та же: регистр накопления «Остатки товаров» (или «ТоварыНаСкладах») и его виртуальная таблица Остатки. Разница в деталях: у товаров обычно два измерения — Номенклатура и Склад, а ресурс — Количество. Собеседующий проверяет, умеете ли вы правильно задать дату среза и отфильтровать «пустые» строки, которые виртуальная таблица иногда возвращает.
Минимальный запрос остатков
Сначала — базовый вариант: остатки всей номенклатуры по всем складам на дату отчёта.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.Склад КАК Склад,
| Остатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатка, ) КАК Остатки";
Запрос.УстановитьПараметр("ДатаОстатка", КонецДня(ДатаОстатка));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Номенклатура.Наименование
+ " (" + Выборка.Склад.Наименование + "): "
+ Выборка.Количество);
КонецЦикла;
Первый параметр таблицы Остатки — это Период (момент, на который считаем остаток). Второй параметр — условие отбора; мы оставили его пустым (после запятой ничего), значит берём остатки по всей номенклатуре и всем складам. Ресурс называется КоличествоОстаток — снова суффикс Остаток, как и в задаче про долги.
Параметр Период: в чём подвох
Главная ловушка этой задачи — время внутри даты. В 1С дата — это на самом деле дата и время. Если написать УстановитьПараметр("ДатаОстатка", Дата(2026, 2, 1)), платформа поймёт это как «01.02.2026 00:00:00» — самое начало суток. Значит, все документы, проведённые 1 февраля в течение дня, в остаток не войдут. Обычно нужно наоборот — «на конец 1 февраля, со всеми документами этого дня». Для этого дату оборачивают в КонецДня(): она превращается в «01.02.2026 23:59:59».
// Остаток на НАЧАЛО дня (документы за 1 февраля НЕ учтены):
Запрос.УстановитьПараметр("ДатаОстатка", Дата(2026, 2, 1));
// Остаток на КОНЕЦ дня (документы за 1 февраля учтены) — обычно нужно это:
Запрос.УстановитьПараметр("ДатаОстатка", КонецДня(Дата(2026, 2, 1)));
// Актуальный остаток «прямо сейчас» — пустой параметр Период:
// РегистрНакопления.ТоварыНаСкладах.Остатки( , ) КАК Остатки
Отдельно запомните: если совсем не передавать первый параметр (оставить пустым), платформа вернёт остаток на текущий момент — самые свежие данные. Это удобно для отчёта «что на складе сейчас», но для отчёта «на 1-е число» так делать нельзя.
Оставляем только положительные остатки
Виртуальная таблица Остатки по умолчанию не показывает позиции с нулевым остатком — платформа их сворачивает. Но она вполне может вернуть строку с нулём или даже с отрицательным количеством, если по товару были движения, а итог обнулился или ушёл в минус (например, из-за ошибок учёта). На собеседовании часто просят: «Покажи только то, что реально есть на складе», то есть строки с положительным остатком. Добавляем ГДЕ:
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
Остатки.Склад КАК Склад,
Остатки.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатка, ) КАК Остатки
ГДЕ
Остатки.КоличествоОстаток > 0
УПОРЯДОЧИТЬ ПО
Номенклатура,
Склад
Здесь ГДЕ Остатки.КоличествоОстаток > 0 — правильное место для условия: мы фильтруем отдельные строки, группировки нет, агрегатов нет. Никакого ИМЕЮЩИЕ тут не нужно. А сортировка по номенклатуре и складу делает вывод предсказуемым и удобным для чтения.
Результат:
Гвозди 3мм Основной склад 1 200
Доска 40х150 Основной склад 75
Доска 40х150 Склад №2 18
Саморез 4х50 Склад №2 9 400
Как это работает
Виртуальная таблица Остатки не хранит данные заранее — платформа рассчитывает её в момент выполнения запроса. Внутри она обращается к специальной таблице итогов регистра и «докручивает» движения до нужной даты. Поэтому так важен параметр Период: он задаёт границу, до которой суммируются приходы и расходы. Условие в параметрах виртуальной таблицы (второй аргумент в скобках) применяется рано — платформа отбирает данные ещё до расчёта итогов, это быстро. Условие в ГДЕ применяется к уже рассчитанному результату. Для отбора по итоговому остатку (КоличествоОстаток > 0) без вариантов используется ГДЕ, а вот отбор по складу или номенклатуре лучше отдавать в параметры таблицы.
Частые ошибки
Дата без КонецДня(). Самая частая ошибка в этой задаче. Отчёт «на 1 февраля» показывает данные на начало суток и «теряет» документы за сам день. Пользователь видит меньше товара, чем есть. Решайте явно, включается последний день или нет, и оборачивайте границу в КонецДня() (или НачалоДня(), если действительно нужно начало).
Путают Период с отбором. Пишут дату во второй параметр вместо первого — и запрос либо падает, либо считает не то. Порядок жёсткий: первый параметр — Период, второй — условие отбора.
Забывают про минусовые остатки. Думают, что Остатки всегда положительны, и не ставят ГДЕ ... > 0. Если в базе есть пересортица или ошибки проведения, в отчёт попадут отрицательные строки, и итог по количеству будет неверным.
Берут остаток из основной таблицы регистра. Иногда новички пишут ИЗ РегистрНакопления.ТоварыНаСкладах без .Остатки(...) и пытаются суммировать движения сами. Это не остаток на дату, а список всех приходов/расходов — совсем другой смысл. Остаток даёт только виртуальная таблица.
Итоги
- Остатки товаров на дату берём из виртуальной таблицы
Остаткирегистра накопления; ресурс —КоличествоОстаток. - Первый параметр таблицы — Период; для «на конец дня» оборачиваем дату в
КонецДня(), для «сейчас» оставляем параметр пустым. - Второй параметр таблицы — условие отбора (по складу/номенклатуре); отбор по итоговому остатку идёт в
ГДЕ. - «Только то, что есть на складе» — это
ГДЕ Остатки.КоличествоОстаток > 0; группировка иИМЕЮЩИЕздесь не нужны. - Никогда не считаем остаток перебором движений — для этого есть виртуальная таблица.