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