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