Как исправить ошибку «{(1,1)}: Ожидается выражение» при проведении расходного ордера в 1С?

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

При разработке и кастомизации конфигураций на платформе «1С:Предприятие 8» разработчики часто сталкиваются со странными ошибками компиляции и отладки. Одной из таких неочевидных проблем является ошибка «{(1,1)}: Ожидается выражение». Чаще всего она возникает при попытке провести документ (например, Расходный кассовый ордер) или в процессе анализа его кода в отладчике.

В этой статье мы подробно разберем, почему возникает эта ошибка, какие критические синтаксические и логические неточности были допущены в исходном коде, и как правильно переписать алгоритм проверки остатков денежных средств, чтобы он работал быстро и безошибочно.

Выясняем причину ошибки «{(1,1)}: Ожидается выражение» в отладчике

Для начала проанализируем поведение платформы во время отладки. Почему в окне вычисления выражений или в табло мы видим эту ошибку? Причина кроется в непонимании принципов работы механизма вычисления выражений 1С:

  1. Попытка вычислить оператор вместо выражения. Отладчик 1С умеет вычислять только логические, арифметические выражения или возвращаемые значения функций (например, ЭтотОбъект.СуммаДокумента или Результат.Количество() > 0). Если мы выделим строку целиком, захватив управляющие конструкции встроенного языка (такие как Если, Тогда, Иначе, КонецЕсли), и нажмем Shift + F9, система выдаст ошибку {(1,1)}: Ожидается выражение. Платформа не может исполнить блок ветвления в окне оценки выражения.
  2. Некорректная передача пустого значения. Если в поле ввода табло случайно попал пустой символ, пробел или недопустимый оператор в первой строке первого символа, платформа вернет ошибку с указанием позиции (1,1).

Решение: Для проверки условий в отладчике необходимо выделять только то, что находится между ключевыми словами. Вместо проверки всей строки Если Результат.Количество() > 0 Тогда, нужно вычислять только выражение Результат.Количество() > 0.

Анализ синтаксических ошибок в исходном коде

Теперь рассмотрим исходный код, представленный в вопросе. В нем содержатся грубые синтаксические ошибки, которые гарантированно приведут к аварийному завершению работы программы при попытке проведения документа.

Первая критическая ошибка кроется в строке:

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

Во встроенном языке 1С методы Выполнить() (объекта Запрос) и Выгрузить() (объекта РезультатЗапроса) являются функциями. Вызов любых функций и методов в 1С обязательно должен сопровождаться круглыми скобками, даже если у них нет параметров. Если скобки опущены, компилятор воспринимает эти вызовы как обращение к свойствам объектов, которых не существует. Это вызывает ошибку выполнения.

Вторая синтаксическая ошибка находится в строке установки параметра даты:

Запрос.УстановитьПараметр("НаДату", ?(Дата = НачалоДня(ТекущаяДата), ТекущаяДата, Дата));

Здесь системная функция ТекущаяДата() вызвана без круглых скобок. Компьютер воспримет ТекущаяДата как неопределенную переменную, что приведет к ошибке компиляции модуля.

Логические и методологические ошибки в запросе

Даже если мы исправим синтаксис встроенного языка, сам запрос написан крайне неоптимально и содержит логические изъяны. Проанализируем их подробнее.

1. Неправильное использование функции ЕстьNULL при агрегировании

На форумах часто предлагают заменить строку выбора суммы на СУММА(ЕстьNULL(ДенежныеСредстваОстатки.СуммаОборот, 0)). Рассмотрим, почему это методологически неверно. Если по заданным фильтрам (касса, тип денежных средств) в базе вообще не окажется записей, то запрос вернет пустую строку, а групповая функция СУММА вернет значение NULL для всего поля, несмотря на то, что внутри нее проверялись значения. Чтобы гарантированно защитить код от получения значения NULL, функцию проверки нужно ставить поверх агрегатной функции:

ЕстьNULL(СУММА(ДенежныеСредстваОстатки.СуммаОборот), 0) КАК СуммаОстаток

2. Использование тяжелой таблицы ОстаткиИОбороты

Для проверки текущего остатка перед списанием в коде используется виртуальная таблица РегистрНакопления.ДенежныеСредства.ОстаткиИОбороты с детализацией до регистратора. Это грубейшая ошибка проектирования. Обращение к таблице остатков и оборотов с периодичностью «Регистратор» заставляет СУБД выполнять сложнейшие соединения физических таблиц итогов и движений. Для проверки остатков всегда нужно использовать легкую виртуальную таблицу Остатки.

3. Неоптимальная фильтрация по Регистратору в предложении ГДЕ

Условие ДенежныеСредстваОстатки.Регистратор <> &Регистратор в секции ГДЕ заставляет СУБД сначала выбрать абсолютно все записи из базы данных, а уже затем отфильтровать их по условию. Это приводит к колоссальным нагрузкам на сервер. Для того чтобы при перепроведении документа исключить его собственные движения из остатка, в 1С применяется механизм Границы периода с исключением момента времени документа.

Правильное решение и оптимизация кода

Давайте объединим все исправления и перепишем этот алгоритм в соответствии со стандартами разработки «1С:Предприятие». Мы заменим виртуальную таблицу на Остатки, применим объект Граница для корректного исключения движений текущего документа и исправим все синтаксические ошибки встроенного языка.

Рассмотрим по шагам правильную реализацию проверки лимита кассы:


// Инициализируем объект запроса
Запрос = Новый Запрос;

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

// Установка параметров
Запрос.УстановитьПараметр("СтруктурнаяЕдиница", ЭтотОбъект.СтруктурнаяЕдиница);
Запрос.УстановитьПараметр("ТипДенежныхСредств", Перечисления.ТипыДенежныхСредств.Наличные);
Запрос.УстановитьПараметр("Касса", Касса);

// Определяем момент времени контроля остатков. 
// Если документ уже записан в базу, используем Границу "Исключая" его собственный МоментВремени.
// Если это новый документ, проверяем остатки на указанную дату документа.
Если ЗначениеЗаполнено(Ссылка) Тогда
    ГраницаКонтроля = Новый Граница(МоментВремени(), ВидГраницы.Исключая);
Иначе
    ГраницаКонтроля = ?(Дата = НачалоДня(ТекущаяДата()), ТекущаяДата(), Дата);
КонецЕсли;

Запрос.УстановитьПараметр("НаДату", ГраницаКонтроля);

// Выполняем запрос с обязательным использованием круглых скобок для вызова методов
РезультатЗапроса = Запрос.Выполнить();
ТаблицаОстатков = РезультатЗапроса.Выгрузить();

// Проверяем результат выполнения запроса
Если ТаблицаОстатков.Количество() = 0 ИЛИ ТаблицаОстатков[0].СуммаОстаток < ЭтотОбъект.СуммаДокумента Тогда
    Отказ = Истина;
    
    // Рассчитываем текущий баланс для вывода сообщения пользователю
    ТекущийБаланс = ?(ТаблицаОстатков.Количество() = 0, 0, ТаблицаОстатков[0].СуммаОстаток);
    
    Сообщение = Новый СообщениеПользователю;
    Сообщение.Текст = "Недостаточно денежных средств для проведения операции! Текущий остаток кассы: " + Строка(ТекущийБаланс);
    Сообщение.Сообщить();
КонецЕсли;

Проанализируем преимущества обновленного кода:

← На главную