Как исправить ошибку «Переменная не определена» при обходе результата запроса в 1С?

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

При разработке алгоритмов проведения документов начинающие разработчики часто сталкиваются с сообщением синтаксического контроля платформы {Документ.РасходнаяНакладная.МодульОбъекта(141,18)}: Переменная не определена (ИмяПеременной). Разберем детально, почему возникает данная ситуация, как найти скрытые опечатки и какие еще критические ошибки встречаются в коде проведения по партионному учету.

Причина ошибки: синтаксическая опечатка в идентификаторе

Проанализируем фрагмент кода, вызвавший исключение при проверке модуля на сервере:


ВыборкаНоменклатура = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Номенклатура");
Пока ВыборкаНоменклатура.Следующий() Цикл
    НадоСписать = ВыборкаНомеклатура.Количество;
    Выборка = ВыборкаНоменклатура.Выбрать();
    // ...
КонецЦикла;

Выясним причину ошибки: переменная инициализируется с именем ВыборкаНоменклатура, а внутри цикла происходит обращение к ВыборкаНомеклатура (пропущена буква «н»). Для компилятора 1С это совершенно новый, нигде ранее не объявленный идентификатор, что и вызывает ошибку Переменная не определена.

Чтобы избежать подобных проблем при написании программного кода, выполним следующие рекомендации:

  1. Используем автодополнение (Ctrl + Пробел): при наборе имен переменных всегда нажимаем комбинацию клавиш автодополнения кода, что исключает механические опечатки.
  2. Проверяем смешение раскладок клавиатуры: внешне одинаковые буквы кириллицы и латиницы (например, «С», «а», «о», «р», «е», «х») являются разными символами для платформы. Если переписать имя переменной заново вручную в одной раскладке или скопировать его из строки объявления, проблема исчезнет.
  3. Очищаем код от спецсимволов: при копировании текста с сайтов или форумов в модуль могут попадать неразрывные пробелы (символ с кодом 160) и невидимые символы форматирования.

Исправление сопутствующих ошибок в запросе и алгоритме проведения

Устранение опечатки в имени выборки — лишь первый шаг. Рассмотрим другие критические ошибки в представленном алгоритме партионного списания, которые приведут к сбоям при проведении документа в реальной базе данных.

  1. Отсутствие отбора по ссылке текущего документа: в первой секции запроса к табличной части Документ.РасходнаяНакладная.СписокНоменклатуры не установлено условие ГДЕ Ссылка = &Ссылка. Из-за этого запрос пытается обработать строки вообще всех документов базы данных.
  2. Вызов методов без скобок: конструкции вида Запрос.Выполнить и ВыборкаНоменклатура.Выбрать синтаксически неверны. В платформе 1С вызов функций обязательно требует наличия круглых скобок: Запрос.Выполнить() и ВыборкаНоменклатура.Выбрать().
  3. Флаг записи движений: для сохранения сформированных записей в регистре накопления перед завершением процедуры необходимо выставить свойство Движения.СебаНоменклатуры.Записывать = Истина;.
  4. Очистка движений перед контролем остатков: при перепроведении документа его старые проводки исказят актуальные остатки. Поэтому в начале процедуры проведения вызывается Движения.СебаНоменклатуры.Записать();.
  5. Контроль нехватки остатков: если на складе не хватает товара, цикл по партиям завершится, но значение НадоСписать останется больше нуля. Необходимо отслеживать данную ситуацию, информировать пользователя и устанавливать Отказ = Истина.
  6. Корректный расчет себестоимости: расчет суммы списания по формуле ?(Списываем = Выборка.КолОст, Выборка.СуммаОст, ...) при нулевом остатке партии (когда Списываем = 0 и КолОст = 0) ошибочно спишет всю сумму остатка. Расчет должен производиться строго при Списываем > 0.

Итоговый корректный вариант процедуры проведения

Соберем правильный вариант алгоритма списания с контролем остатков и корректной работой с выборками запроса:


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

Посмотрим на ключевые изменения: выборка по группировкам ВыборкаНоменклатура содержит агрегированные итоги по каждой позиции номенклатуры, исключены синтаксические ошибки обращения к методам, добавлен корректный расчет себестоимости и проверка доступного остатка перед формированием записей регистра.

← На главную