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