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

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

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

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

Причина ошибки: Разделение контекста выполнения (Клиент и Сервер)

Для того чтобы понять, почему платформа выдает ошибку, рассмотрим архитектуру управляемого приложения. Код в 1С физически выполняется в разных контекстах:

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

Критическая проблема транзакций БД и блокировок

Попытка вызвать диалоговое окно (задать вопрос пользователю) внутри серверных событий записи объекта (таких как ПередЗаписьюНаСервере в форме или ПередЗаписью в модуле объекта) — это не просто синтаксическая ошибка, это грубейшее нарушение стандартов разработки.

Давайте проанализируем, что происходит на уровне СУБД во время записи документа:

  1. Платформа открывает транзакцию базы данных.
  2. Блокируются таблицы и записи, связанные с записываемым документом (чтобы другие пользователи не могли одновременно изменить те же данные).
  3. Выполняются проверки в обработчиках событий записи на сервере.
  4. Если бы платформа позволила остановить серверный код и ожидать ответа пользователя на вопрос, транзакция оставалась бы открытой всё то время, пока пользователь думает или отошел от компьютера.
  5. Заблокированные таблицы парализовали бы работу всех остальных пользователей системы. Они получали бы ошибку: «Превышено время ожидания предоставления блокировки».

Золотое правило разработки в 1С: Любые интерактивные действия, вопросы, диалоги и предупреждения должны выполняться строго ДО начала транзакции записи и только на клиенте.

Отказ от модальности (Современный стандарт 1С)

В старых версиях платформы использовался метод Вопрос(), который полностью останавливал выполнение кода до получения ответа. В современных конфигурациях (особенно работающих в веб-клиенте или облаке) включен режим работы «Без использования модальности».

Использование синхронных методов вроде Вопрос() вызовет системную ошибку выполнения. Вместо них необходимо применять асинхронный метод ПоказатьВопрос(), работающий через механизм ОписаниеОповещения. Этот метод не останавливает выполнение кода, а принимает в качестве параметра процедуру-обработчик, которая сработает только тогда, когда пользователь сделает свой выбор.

Правильный паттерн реализации: схема «Клиент — Сервер — Клиент»

Для реализации проверки с вопросом перед записью используется стандартный паттерн обхода интерактивных событий. Алгоритм выглядит следующим образом:

  1. Пользователь нажимает кнопку «Записать» или «Провести».
  2. Срабатывает клиентское событие формы ПередЗаписью.
  3. Программа проверяет, не был ли этот вопрос уже задан ранее (с помощью специального флага в параметрах записи).
  4. Если проверка еще не проводилась:
    • Программа выполняет быстрый серверный вызов (например, через функцию &НаСервереБезКонтекста), чтобы проверить условия (в нашем примере — проверить остаток долга контрагента).
    • Если условия нарушены, запись принудительно прерывается установкой параметра Отказ = Истина.
    • На клиенте асинхронно вызывается метод ПоказатьВопрос().
  5. Если пользователь отвечает «Да» (согласен продолжить запись):
    • В структуру параметров записи добавляется признак успешного прохождения проверки.
    • Программа повторно инициирует запись объекта методом Записать().
    • При повторном входе в событие ПередЗаписью система видит наш флаг, пропускает проверку, и документ успешно записывается на сервере без лишних вопросов.

Пошаговый пример реализации в модуле формы документа

Рассмотрим, как правильно написать этот код в модуле управляемой формы вашего документа. Мы полностью перенесем логику проверки долга контрагента на сервер в неконтекстную функцию, а диалог организуем на клиенте.


&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    
    // 1. Проверяем, не проходили ли мы эту проверку ранее в текущем сеансе записи.
    // Если флаг установлен — пропускаем дальнейшие проверки и пишем документ.
    Если ПараметрыЗаписи.Свойство("ПроверкаДолгаВыполнена") Тогда
        Возврат;
    КонецЕсли;
    
    // 2. Проверку выполняем только при проведении документа.
    // Если документ просто записывается, проверку можно пропустить (или оставить по вашему усмотрению).
    Если ПараметрыЗаписи.РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        
        // Получаем необходимые данные из реквизитов формы
        ИмяКонтрагента = Объект.Контрагент.Наименование;
        СсылкаНаКонтрагента = Объект.Контрагент;
        СуммаДокумента = Объект.СуммаДокумента;
        ДатаДокумента = Объект.Дата;
        
        // 3. Вызываем серверный неконтекстный метод для проверки задолженности.
        // Это быстрое чтение базы данных без открытия транзакции записи.
        Если КонтрагентИмеетПревышениеЛимита(СсылкаНаКонтрагента, СуммаДокумента, ДатаДокумента) Тогда
            
            // Отменяем текущую попытку записи документа
            Отказ = Истина;
            
            // Формируем описание оповещения для обработки ответа пользователя
            ПараметрыОповещения = Новый ОписаниеОповещения("ПослеОтветаНаВопросОДолге", ЭтотОбъект, ПараметрыЗаписи);
            
            // Задаем вопрос асинхронно
            ТекстВопроса = НСтр("ru = 'У контрагента ""%1"" сумма долга меньше суммы документа. Всё равно провести документ?'");
            ТекстВопроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстВопроса, ИмяКонтрагента);
            
            ПоказатьВопрос(ПараметрыОповещения, ТекстВопроса, РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет);
            
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Процедура ПослеОтветаНаВопросОДолге(Результат, ДополнительныеПараметры) Экспорт
    
    // 4. Анализируем ответ пользователя из диалогового окна
    Если Результат = КодВозвратаДиалога.Да Тогда
        
        // Создаем копию параметров записи и добавляем туда наш флаг-разрешение
        НовыеПараметрыЗаписи = Новый Структура;
        Для Каждого Свойство Из ДополнительныеПараметры Цикл
            НовыеПараметрыЗаписи.Вставить(Свойство.Ключ, Свойство.Значение);
        КонецЦикла;
        
        // Устанавливаем флаг, сигнализирующий, что пользователь одобрил проведение
        НовыеПараметрыЗаписи.Вставить("ПроверкаДолгаВыполнена", Истина);
        
        // 5. Повторно запускаем запись документа с обновленной структурой параметров
        ЭтотОбъект.Записать(НовыеПараметрыЗаписи);
        
    КонецЕсли;
    
КонецПроцедуры

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

КонецФункции

Преимущества предложенного решения

Разберем, почему данный подход является наиболее эффективным и рекомендуется стандартами разработки фирмы «1С»:

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

← На главную