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