В процессе разработки расширений или модификации типовых конфигураций программисты часто сталкиваются с ситуацией, когда код, успешно работавший вчера, сегодня отказывается компилироваться. Одной из самых коварных ошибок является «Неопознанный оператор &НаСервере» (или &НаКлиенте). На первый взгляд синтаксис кажется верным, но система упорно выдает ошибку инициализации модуля. Разберем подробно, почему это происходит, как найти «невидимую» причину и как попутно улучшить архитектуру вашего кода.
Когда конфигуратор выдает ошибку «Неопознанный оператор» на строке с директивой компиляции, это означает, что парсер платформы 1С встретил нечто, что он не может интерпретировать как начало процедуры или функции. Рассмотрим основные причины:
& могут попасть неразрывные пробелы, символы табуляции или невидимые маркеры кодировки (BOM). Даже один лишний знак превращает директиву &НаСервере в бессмысленный набор символов для системы.&НаСервере, &НаКлиенте, &НаСервереБезКонтекста допустимы только в модулях управляемых форм и модулях команд. Если вы случайно пытаетесь использовать их в общем модуле, модуле объекта или менеджера, система их не узнает, так как контекст исполнения в этих модулях задается свойствами самого объекта метаданных.КонецПроцедуры, компилятор воспримет следующую директиву как часть тела предыдущего метода, что недопустимо.Проанализируем ситуацию автора темы: после удаления текста перед &НаСервере и повторного написания директивы проблема исчезла. Это подтверждает наличие «мусорного» символа, который визуально не определялся в редакторе кода.
В представленном коде автор пытается обновлять реквизит ОтветственныйМенеджер при каждом открытии формы документа:
&НаСервере
Процедура Расш1_ПриОткрытииПередНаСервере
// ... получение данных через запрос ...
Если Объект.ОтветственныйМенеджер <> Выборка.ОсновнойМенеджер Тогда
Объект.ОтветственныйМенеджер = Выборка.ОсновнойМенеджер;
ЭтотОбъект.Записать; // Опасный момент!
КонецЕсли;
КонецПроцедуры
Разберем, почему вызов ЭтотОбъект.Записать() внутри события ПриОткрытии считается плохой практикой (моветоном):
Если нам необходимо обновить данные при создании новой формы, проанализируем разницу между событиями ПриСозданииНаСервере и ПриОткрытии.
ПриСозданииНаСервере — это оптимальное место для серверной логики. Это событие выполняется на сервере до передачи формы клиенту. Здесь мы можем заполнить реквизиты объекта, не делая лишних клиент-серверных вызовов (Roundtrip). Если мы вызовем серверную процедуру из клиентского события ПриОткрытии, как в исходном примере, мы заставим систему гонять данные между клиентом и сервером лишний раз.
Рассмотрим правильную структуру заполнения через расширение:
&НаСервере
&После("ПриСозданииНаСервере")
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
// Выполняем проверку только для существующих документов
Если ЗначениеЗаполнено(Объект.Ссылка) Тогда
// Здесь мы только заполняем данные в форме, но НЕ записываем объект в БД!
// Пользователь сам решит, нажимать "Записать" или нет.
МенеджерИзСправочника = Объект.Партнер.ОсновнойМенеджер;
Если Объект.ОтветственныйМенеджер <> МенеджерИзСправочника Тогда
Объект.ОтветственныйМенеджер = МенеджерИзСправочника;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В обсуждении возник спор о том, как лучше получать данные связанного справочника. Автор использовал сложный запрос с ЛЕВОЕ СОЕДИНЕНИЕ. Рассмотрим альтернативу — обращение через точку в запросе:
Запрос.Текст = "ВЫБРАТЬ
| Док.Партнер.ОсновнойМенеджер КАК Менеджер
|ИЗ
| Документ.эсЗаданиеСотруднику КАК Док
|ГДЕ
| Док.Ссылка = &Ссылка";
Выясним причину споров: существует мнение, что обращение через точку в запросах 1С неоптимально. Однако это верно только для составных типов данных. Если поле Партнер имеет четко заданный тип СправочникСсылка.Партнеры, то платформа 1С на уровне SQL сформирует точно такое же LEFT JOIN, как если бы вы прописали его вручную. Такой код читается легче и сокращает вероятность ошибки в тексте запроса. Явное соединение стоит использовать в сложных отчетах или при работе с реквизитами неопределенного типа.
Иногда лучшим решением является отказ от хранения избыточных данных. Если ОтветственныйМенеджер всегда должен быть равен основному менеджеру партнера, проанализируем возможность отображения этого поля без записи в документ:
Объект, раскройте его, найдите Партнер и раскройте его свойства.ОсновнойМенеджер (из состава реквизитов Партнера) прямо на форму.В этом случае система будет автоматически подтягивать и отображать актуального менеджера из справочника при открытии формы, при этом в самом документе ничего хранить и перезаписывать не придется. Это самый надежный способ избежать расхождения данных.
Подведем итог: если вы столкнулись с ошибкой неопознанного оператора, первым делом очистите строку с директивой и перепишите её вручную. А при проектировании логики обновления реквизитов всегда отдавайте предпочтение событию ПриСозданииНаСервере и избегайте принудительной записи объекта без участия пользователя.