При разработке в 1С:Предприятие на управляемых формах начинающие и даже опытные программисты часто сталкиваются с ситуацией, когда платформа отказывается "видеть" созданный общий модуль. В результате при попытке обратиться к процедуре модуля система выдает ошибку: Переменная не определена. Рассмотрим подробнее, почему так происходит, выясним причину такого поведения платформы и разберем по шагам, как правильно настроить свойства общих модулей для корректной работы.
Посмотрим на пример, который часто встречается в учебных материалах (например, в пособиях Радченко) и реальных задачах. У нас есть форма документа, и при изменении количества или цены в табличной части мы хотим пересчитать сумму строки, обратившись к алгоритму в общем модуле.
Код обработчика события на клиенте выглядит следующим образом:
&НаКлиенте
Процедура изделияКоличествоПриИзменении()
СтрокаТаблицы = Элементы.Изделия.ТекущиеДанные;
СобытияДокументов.ВычислитьСумму(СтрокаТаблицы);
КонецПроцедуры
Код в самом общем модуле СобытияДокументов:
Процедура ВычислитьСумму(СтрокаТаблицы) Экспорт
СтрокаТаблицы.Сумма = СтрокаТаблицы.Количество * СтрокаТаблицы.Цена;
КонецПроцедуры
При проверке модуля формы или в режиме предприятия мы получаем ошибку компиляции: {Документ.ПриходнаяНакладная.Форма.ФормаДокумента.Форма(6,1)}: Переменная не определена (СобытияДокументов). Платформа воспринимает имя общего модуля как неизвестную переменную. Разберем, почему это происходит.
В архитектуре 1С:Предприятие 8.3 четко разделен контекст выполнения кода: клиент (управляемое приложение, веб-клиент) и сервер. Когда мы пишем код с директивой &НаКлиенте, платформа ожидает, что все вызываемые объекты и модули будут доступны именно в клиентском контексте.
Если код обращается к какому-либо имени (в нашем случае СобытияДокументов), и платформа не находит в текущем контексте ни локальной переменной, ни доступной функции, ни видимого общего модуля с таким именем, она выдает ошибку «Переменная не определена». Общий модуль может быть "невидим" для формы по нескольким причинам:
ВычислитьСумму()). Однако использовать глобальные модули без крайней необходимости категорически не рекомендуется.Для исправления ошибки нам необходимо правильно расставить флажки в свойствах общего модуля. Проанализируем, какие вычисления выполняются внутри процедуры. В нашем примере это простое перемножение двух чисел (количества и цены). Обращение к базе данных или серверным объектам не требуется. Следовательно, оптимально выполнять этот код на стороне клиента, чтобы не нагружать сервер лишними вызовами.
Шаги для решения:
СобытияДокументов.Если бы алгоритм требовал выполнения запросов к базе данных, нам пришлось бы оставить флаг Сервер и дополнительно установить флаг Вызов сервера, чтобы клиентский код мог "достучаться" до этого модуля. Но для простых математических операций это излишне.
Рассмотрим еще один важный аспект, затронутый в обсуждении. В исходном коде переменная СтрокаТаблицы получает тип ДанныеФормыЭлементКоллекции и передается в процедуру общего модуля целиком.
Если процедура выполняется на клиенте, передача строки табличной части допустима. Однако, если бы мы вызывали серверную процедуру (с флагом Вызов сервера), передавать туда объекты интерфейса целиком — это плохая практика. Почему? Потому что это вызывает лишние затраты ресурсов на сериализацию (перевод объемных данных формы в формат для передачи по сети на сервер и обратно).
Правильный подход — создать структуру, поместить в нее только нужные для расчета данные, передать на сервер, а обратно получить готовый результат. Посмотрим на пример такого кода:
// В модуле формы на клиенте:
&НаКлиенте
Процедура изделияКоличествоПриИзменении(Элемент)
СтрокаТЧ = Элементы.Изделия.ТекущиеДанные;
// Создаем структуру только с необходимыми данными
ДанныеДляРасчета = Новый Структура;
ДанныеДляРасчета.Вставить("Количество", СтрокаТЧ.Количество);
ДанныеДляРасчета.Вставить("Цена", СтрокаТЧ.Цена);
// Вызываем серверную функцию (если расчет сложный)
СтрокаТЧ.Сумма = ОбщийМодульСервер.РассчитатьСумму(ДанныеДляРасчета);
КонецПроцедуры
// В серверном общем модуле:
Функция РассчитатьСумму(ДанныеРасчета) Экспорт
Возврат ДанныеРасчета.Количество * ДанныеРасчета.Цена;
КонецФункции
Проанализируем еще одну классическую задачу, с которой сталкиваются при разработке подобных алгоритмов расчета строк — расчет суммы НДС. На первый взгляд, это простая операция, но здесь кроется математическая ловушка.
Если мы будем считать НДС для каждой отдельной строки, округлять до копеек, а затем сложим сумму НДС по всем строкам документа, итоговое значение почти всегда не совпадет с суммой НДС, если бы мы рассчитали её от общей суммы документа в целом. Разница возникает из-за эффекта математического округления и может составлять от 1 копейки до нескольких рублей, если строк в документе много.
Как эта задача решается в типовых конфигурациях 1С:
В типовых конфигурациях для этого используются готовые функции общих модулей, например, функция распределения пропорционально, которая принимает массив сумм и выравнивает итоги так, чтобы данные сходились до копейки и не вызывали вопросов у бухгалтеров.
Подводя итоги: чтобы избежать ошибки неопределенной переменной при вызове общего модуля, всегда внимательно проверяйте контекст, из которого вы к нему обращаетесь, и соответствие флагов модуля этому контексту. Используйте передачу структур для оптимизации клиент-серверных вызовов и помните о правилах распределения погрешностей при работе с финансовыми показателями и налогами.