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