Почему запрос к остаткам регистра накопления возвращает нулевой остаток при проверке документа

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При реализации алгоритмов проведения документов одной из частых задач является контроль остатков: проверка наличия денежных средств на счете или товаров на складе. Нередко разработчики сталкиваются с ситуацией, когда данные в регистре фактически присутствуют, однако запрос к виртуальной таблице остатков упорно возвращает 0, из-за чего проверка ошибочно сообщает о нехватке средств. Разберем по шагам, почему возникает эта ошибка и как корректно организовать контроль остатков в 1С.

Причина 1: Несоответствие типов данных и использование строк в измерениях

Проанализируем типовую структуру запроса контроля остатков, когда данные документа соединяются с виртуальной таблицей РегистрНакопления.ОстатокДенежныхСредств.Остатки через ЛЕВОЕ СОЕДИНЕНИЕ по полю счета. Если связь не срабатывает, поле СуммаОстаток принимает значение NULL, которое с помощью функции ЕСТЬNULL(..., 0) превращается в ноль.

Основная причина такого поведения — использование примитивного типа Строка для измерения регистра и реквизита документа вместо ссылочного типа (например, справочника БанковскиеСчета или Кассы). При работе со строками возникают следующие проблемы:

  1. Различия в длине и типе строки: если в документе строка переменной длины, а в измерении регистра — фиксированной (или разной длины), СУБД дополняет одну из них пробелами, и прямое равенство ВложенныйЗапрос.ОплатаСоСчета = ОстатокДенежныхСредствОстатки.Счет не выполняется.
  2. Невидимые символы и пробелы: случайный пробел в начале или конце наименования счета в документе делает его не равным значению в регистре.
  3. Разный регистр символов: в зависимости от настроек базы данных сравнение строк может быть чувствительно к регистру.

Решение: переведите измерение регистра и реквизит документа на ссылочный тип данных (Справочник). Если по каким-то причинам требуется оставить строку, убедитесь, что длина и тип строк (фиксированная/переменная) в метаданных полностью совпадают, а при записи значений выполняется функция СокрЛП().

Причина 2: Некорректная передача момента времени в параметры виртуальной таблицы

Посмотрим на параметры виртуальной таблицы: передача простого значения реквизита Дата в качестве первого параметра Остатки(&Дата, ...) считывает остатки строго на начало секунды даты документа. Все движения, записанные в эту же секунду (включая движения самого документа или параллельных документов), не попадают в расчет.

В процедуре ОбработкаПроведения для получения корректного среза на момент документа необходимо передавать не Дата, а объект МоментВремени() или Граница:


ГраницаВключая = Новый Граница(МоментВремени(), ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", ГраницаВключая);

Причина 3: Неоптимальная выборка и отсутствие отбора в параметрах виртуальной таблицы

Рассмотрим текст запроса. Использование конструкции ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстатокДенежныхСредств.Остатки(&Дата, ) без второго параметра приводит к тому, что платформа вычисляет остатки по всему регистру накопления, а уже затем соединяет их с документом. Это существенно снижает производительность.

Правильный подход — ограничить выборку в самой виртуальной таблице и использовать временные таблицы вместо вложенных запросов. Посмотрим на оптимизированный вариант запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ДокументТаблица.ОплатаСоСчета КАК ОплатаСоСчета,
|	СУММА(ДокументТаблица.СуммаОплаты) КАК СуммаОплаты
|ПОМЕСТИТЬ ВТ_Документ
|ИЗ
|	Документ.ПриемТовараНаСклад КАК ДокументТаблица
|ГДЕ
|	ДокументТаблица.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
|	ДокументТаблица.ОплатаСоСчета
|ИНДЕКСИРОВАТЬ ПО
|	ОплатаСоСчета
|;
|
|ВЫБРАТЬ
|	ВТ_Документ.ОплатаСоСчета КАК ОплатаСоСчета,
|	ВТ_Документ.СуммаОплаты КАК СуммаОплаты,
|	ЕСТЬNULL(Остатки.СуммаОстаток, 0) КАК СуммаОстаток
|ИЗ
|	ВТ_Документ КАК ВТ_Документ
|	ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстатокДенежныхСредств.Остатки(
|		&Период,
|		Счет В (ВЫБРАТЬ ВТ_Документ.ОплатаСоСчета ИЗ ВТ_Документ)
|	) КАК Остатки
|	ПО ВТ_Документ.ОплатаСоСчета = Остатки.Счет";

Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("Период", Новый Граница(МоментВремени(), ВидГраницы.Исключая));

Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();

Пока Выборка.Следующий() Цикл
	Если Выборка.СуммаОплаты > Выборка.СуммаОстаток Тогда
		Отказ = Истина;
		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = "На счете " + Строка(Выборка.ОплатаСоСчета) + " недостаточно средств. Доступно: " + Строка(Выборка.СуммаОстаток) + ", требуется: " + Строка(Выборка.СуммаОплаты);
		Сообщение.Сообщить();
	КонецЕсли;
КонецЦикла;

Причина 4: Отключение или неактуальность итогов регистра

Если типы данных совпадают и запрос составлен корректно, но остаток все равно равен нулю, проанализируем состояние итогов регистра накопления. Если в информационной базе выключены текущие итоги или база была некорректно обновлена, виртуальная таблица Остатки не возвращает актуальные данные.

Для устранения этой проблемы выполните пересчет итогов в режиме Конфигуратора через меню Администрирование -> Тестирование и исправление информационной базы (с установленным флагом Пересчет итогов) либо программно с помощью объекта РегистрыНакопления.ОстатокДенежныхСредств.ПересчитатьИтоги().

← На главную